All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-04-28  0:20 ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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.

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 (3):
  dpll: Add DPLL framework base functions
  dpll: documentation on DPLL subsystem interface
  ptp_ocp: implement DPLL ops

 Documentation/dpll.rst                        |  408 ++++
 Documentation/netlink/specs/dpll.yaml         |  472 ++++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    8 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |   10 +
 drivers/dpll/dpll_core.c                      |  939 ++++++++
 drivers/dpll/dpll_core.h                      |  113 +
 drivers/dpll/dpll_netlink.c                   |  991 +++++++++
 drivers/dpll/dpll_netlink.h                   |   27 +
 drivers/dpll/dpll_nl.c                        |  126 ++
 drivers/dpll/dpll_nl.h                        |   42 +
 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     | 1929 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |  101 +
 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    |  438 ++++
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  327 ++-
 include/linux/dpll.h                          |  294 +++
 include/linux/mlx5/driver.h                   |    2 +
 include/linux/mlx5/mlx5_ifc.h                 |   59 +-
 include/linux/netdevice.h                     |    7 +
 include/uapi/linux/dpll.h                     |  204 ++
 include/uapi/linux/if_link.h                  |    2 +
 net/core/dev.c                                |   20 +
 net/core/rtnetlink.c                          |   38 +
 41 files changed, 7966 insertions(+), 59 deletions(-)
 create mode 100644 Documentation/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.34.1


^ permalink raw reply	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-04-28  0:20 ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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.

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 (3):
  dpll: Add DPLL framework base functions
  dpll: documentation on DPLL subsystem interface
  ptp_ocp: implement DPLL ops

 Documentation/dpll.rst                        |  408 ++++
 Documentation/netlink/specs/dpll.yaml         |  472 ++++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    8 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |   10 +
 drivers/dpll/dpll_core.c                      |  939 ++++++++
 drivers/dpll/dpll_core.h                      |  113 +
 drivers/dpll/dpll_netlink.c                   |  991 +++++++++
 drivers/dpll/dpll_netlink.h                   |   27 +
 drivers/dpll/dpll_nl.c                        |  126 ++
 drivers/dpll/dpll_nl.h                        |   42 +
 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     | 1929 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |  101 +
 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    |  438 ++++
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  327 ++-
 include/linux/dpll.h                          |  294 +++
 include/linux/mlx5/driver.h                   |    2 +
 include/linux/mlx5/mlx5_ifc.h                 |   59 +-
 include/linux/netdevice.h                     |    7 +
 include/uapi/linux/dpll.h                     |  204 ++
 include/uapi/linux/if_link.h                  |    2 +
 net/core/dev.c                                |   20 +
 net/core/rtnetlink.c                          |   38 +
 41 files changed, 7966 insertions(+), 59 deletions(-)
 create mode 100644 Documentation/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.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
 drivers/dpll/dpll_nl.c                | 126 +++++++
 drivers/dpll/dpll_nl.h                |  42 +++
 include/uapi/linux/dpll.h             | 202 +++++++++++
 4 files changed, 842 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..67ca0f6cf2d5
--- /dev/null
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -0,0 +1,472 @@
+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
+      attribute
+    entries:
+      -
+        name: unspec
+        doc: unspecified value
+      -
+        name: manual
+        doc: source can be only selected by sending a request to dpll
+      -
+        name: automatic
+        doc: highest prio, valid source, auto selected by dpll
+      -
+        name: holdover
+        doc: dpll forced into holdover mode
+      -
+        name: freerun
+        doc: dpll driven on system clk, no holdover available
+      -
+        name: nco
+        doc: dpll driven by Numerically Controlled Oscillator
+    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: unspec
+        doc: unspecified value
+      -
+        name: unlocked
+        doc: |
+          dpll was not yet locked to any valid source (or is in one of
+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
+      -
+        name: calibrating
+        doc: dpll is trying to lock to a valid signal
+      -
+        name: locked
+        doc: dpll is locked
+      -
+        name: holdover
+        doc: |
+          dpll is in holdover state - lost a valid lock or was forced by
+          selecting DPLL_MODE_HOLDOVER mode
+    render-max: true
+  -
+    type: const
+    name: temp-divider
+    value: 10
+    doc: |
+      temperature divider allowing userspace to calculate the
+      temperature as float with single digit precision.
+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
+      tempearture 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: unspec
+        doc: unspecified value
+      -
+        name: pps
+        doc: dpll produces Pulse-Per-Second signal
+      -
+        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: unspec
+        doc: unspecified value
+      -
+        name: mux
+        doc: aggregates another layer of selectable pins
+      -
+        name: ext
+        doc: external source
+      -
+        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: unspec
+        doc: unspecified value
+      -
+        name: source
+        doc: pin used as a source of a signal
+      -
+        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-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: unspec
+        doc: unspecified value
+      -
+        name: connected
+        doc: pin connected, active source of phase locked loop
+      -
+        name: disconnected
+        doc: pin disconnected, not considered as a valid source
+      -
+        name: selectable
+        doc: pin enabled for automatic source 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
+  -
+    type: enum
+    name: event
+    doc: events of dpll generic netlink family
+    entries:
+      -
+        name: unspec
+        doc: invalid event type
+      -
+        name: device-create
+        doc: dpll device created
+      -
+        name: device-delete
+        doc: dpll device deleted
+      -
+        name: device-change
+        doc: |
+          attribute of dpll device or pin changed, reason is to be found with
+          an attribute type (DPLL_A_*) received with the event
+
+
+attribute-sets:
+  -
+    name: dpll
+    enum-name: dplla
+    attributes:
+      -
+        name: device
+        type: nest
+        value: 1
+        multi-attr: true
+        nested-attributes: device
+      -
+        name: id
+        type: u32
+      -
+        name: dev-name
+        type: string
+      -
+        name: bus-name
+        type: string
+      -
+        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: clock-id
+        type: u64
+      -
+        name: type
+        type: u8
+        enum: type
+      -
+        name: pin-idx
+        type: u32
+      -
+        name: pin-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-parent
+        type: nest
+        multi-attr: true
+        nested-attributes: pin-parent
+      -
+        name: pin-parent-idx
+        type: u32
+      -
+        name: pin-rclk-device
+        type: string
+      -
+        name: pin-dpll-caps
+        type: u32
+  -
+    name: device
+    subset-of: dpll
+    attributes:
+      -
+        name: id
+        type: u32
+        value: 2
+      -
+        name: dev-name
+        type: string
+      -
+        name: bus-name
+        type: string
+      -
+        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: clock-id
+        type: u64
+      -
+        name: type
+        type: u8
+        enum: type
+      -
+        name: pin-prio
+        type: u32
+        value: 19
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+  -
+    name: pin-parent
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-state
+        type: u8
+        value: 20
+        enum: pin-state
+      -
+        name: pin-parent-idx
+        type: u32
+        value: 22
+      -
+        name: pin-rclk-device
+        type: string
+  -
+    name: pin-frequency-range
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-frequency-min
+        type: u64
+        value: 17
+      -
+        name: pin-frequency-max
+        type: u64
+
+operations:
+  list:
+    -
+      name: unspec
+      doc: unused
+
+    -
+      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
+            - bus-name
+            - dev-name
+        reply:
+          attributes:
+            - device
+
+      dump:
+        pre: dpll-pre-dumpit
+        post: dpll-post-dumpit
+        reply:
+          attributes:
+            - device
+
+    -
+      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
+            - bus-name
+            - dev-name
+            - mode
+
+    -
+      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
+            - bus-name
+            - dev-name
+            - pin-idx
+        reply: &pin-attrs
+          attributes:
+            - pin-idx
+            - pin-label
+            - pin-type
+            - pin-direction
+            - pin-frequency
+            - pin-frequency-supported
+            - pin-parent
+            - pin-rclk-device
+            - pin-dpll-caps
+            - device
+
+      dump:
+        pre: dpll-pin-pre-dumpit
+        post: dpll-pin-post-dumpit
+        request:
+          attributes:
+            - id
+            - bus-name
+            - dev-name
+        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
+            - bus-name
+            - dev-name
+            - pin-idx
+            - pin-frequency
+            - pin-direction
+            - pin-prio
+            - pin-state
+            - pin-parent-idx
+
+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..2f8643f401b0
--- /dev/null
+++ b/drivers/dpll/dpll_nl.c
@@ -0,0 +1,126 @@
+// 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 <linux/dpll.h>
+
+/* DPLL_CMD_DEVICE_GET - do */
+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_MODE] = 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_IDX + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
+};
+
+/* DPLL_CMD_PIN_GET - dump */
+static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+};
+
+/* DPLL_CMD_PIN_SET - do */
+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
+};
+
+/* Ops table for dpll */
+static const struct genl_split_ops dpll_nl_ops[] = {
+	{
+		.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_BUS_NAME,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd	= DPLL_CMD_DEVICE_GET,
+		.start	= dpll_pre_dumpit,
+		.dumpit	= dpll_nl_device_get_dumpit,
+		.done	= dpll_post_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_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_IDX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DPLL_CMD_PIN_GET,
+		.start		= dpll_pin_pre_dumpit,
+		.dumpit		= dpll_nl_pin_get_dumpit,
+		.done		= dpll_pin_post_dumpit,
+		.policy		= dpll_pin_get_dump_nl_policy,
+		.maxattr	= DPLL_A_BUS_NAME,
+		.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_IDX,
+		.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..57ab2da562ba
--- /dev/null
+++ b/drivers/dpll/dpll_nl.h
@@ -0,0 +1,42 @@
+/* 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 <linux/dpll.h>
+
+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_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_pre_dumpit(struct netlink_callback *cb);
+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
+int dpll_post_dumpit(struct netlink_callback *cb);
+int dpll_pin_post_dumpit(struct netlink_callback *cb);
+
+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_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..e188bc189754
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,202 @@
+/* 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 sources to syntonize with it, valid values for
+ *   DPLL_A_MODE attribute
+ * @DPLL_MODE_UNSPEC: unspecified value
+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to dpll
+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
+ */
+enum dpll_mode {
+	DPLL_MODE_UNSPEC,
+	DPLL_MODE_MANUAL,
+	DPLL_MODE_AUTOMATIC,
+	DPLL_MODE_HOLDOVER,
+	DPLL_MODE_FREERUN,
+	DPLL_MODE_NCO,
+
+	__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_UNSPEC: unspecified value
+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid source (or
+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid signal
+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid lock or
+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
+ */
+enum dpll_lock_status {
+	DPLL_LOCK_STATUS_UNSPEC,
+	DPLL_LOCK_STATUS_UNLOCKED,
+	DPLL_LOCK_STATUS_CALIBRATING,
+	DPLL_LOCK_STATUS_LOCKED,
+	DPLL_LOCK_STATUS_HOLDOVER,
+
+	__DPLL_LOCK_STATUS_MAX,
+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
+};
+
+#define DPLL_TEMP_DIVIDER	10
+
+/**
+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
+ * @DPLL_TYPE_UNSPEC: unspecified value
+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
+ */
+enum dpll_type {
+	DPLL_TYPE_UNSPEC,
+	DPLL_TYPE_PPS,
+	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_UNSPEC: unspecified value
+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
+ * @DPLL_PIN_TYPE_EXT: external source
+ * @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_UNSPEC,
+	DPLL_PIN_TYPE_MUX,
+	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_UNSPEC: unspecified value
+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
+ */
+enum dpll_pin_direction {
+	DPLL_PIN_DIRECTION_UNSPEC,
+	DPLL_PIN_DIRECTION_SOURCE,
+	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_MHZ	10000000
+
+/**
+ * enum dpll_pin_state - defines possible states of a pin, valid values for
+ *   DPLL_A_PIN_STATE attribute
+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase locked loop
+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
+ *   source
+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
+ */
+enum dpll_pin_state {
+	DPLL_PIN_STATE_UNSPEC,
+	DPLL_PIN_STATE_CONNECTED,
+	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_event - events of dpll generic netlink family
+ * @DPLL_EVENT_UNSPEC: invalid event type
+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed, reason
+ *   is to be found with an attribute type (DPLL_A_*) received with the event
+ */
+enum dpll_event {
+	DPLL_EVENT_UNSPEC,
+	DPLL_EVENT_DEVICE_CREATE,
+	DPLL_EVENT_DEVICE_DELETE,
+	DPLL_EVENT_DEVICE_CHANGE,
+};
+
+enum dplla {
+	DPLL_A_DEVICE = 1,
+	DPLL_A_ID,
+	DPLL_A_DEV_NAME,
+	DPLL_A_BUS_NAME,
+	DPLL_A_MODE,
+	DPLL_A_MODE_SUPPORTED,
+	DPLL_A_LOCK_STATUS,
+	DPLL_A_TEMP,
+	DPLL_A_CLOCK_ID,
+	DPLL_A_TYPE,
+	DPLL_A_PIN_IDX,
+	DPLL_A_PIN_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_PARENT,
+	DPLL_A_PIN_PARENT_IDX,
+	DPLL_A_PIN_RCLK_DEVICE,
+	DPLL_A_PIN_DPLL_CAPS,
+
+	__DPLL_A_MAX,
+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
+};
+
+enum {
+	DPLL_CMD_UNSPEC = 1,
+	DPLL_CMD_DEVICE_GET,
+	DPLL_CMD_DEVICE_SET,
+	DPLL_CMD_PIN_GET,
+	DPLL_CMD_PIN_SET,
+
+	__DPLL_CMD_MAX,
+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
+};
+
+#define DPLL_MCGRP_MONITOR	"monitor"
+
+#endif /* _UAPI_LINUX_DPLL_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
 drivers/dpll/dpll_nl.c                | 126 +++++++
 drivers/dpll/dpll_nl.h                |  42 +++
 include/uapi/linux/dpll.h             | 202 +++++++++++
 4 files changed, 842 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..67ca0f6cf2d5
--- /dev/null
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -0,0 +1,472 @@
+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
+      attribute
+    entries:
+      -
+        name: unspec
+        doc: unspecified value
+      -
+        name: manual
+        doc: source can be only selected by sending a request to dpll
+      -
+        name: automatic
+        doc: highest prio, valid source, auto selected by dpll
+      -
+        name: holdover
+        doc: dpll forced into holdover mode
+      -
+        name: freerun
+        doc: dpll driven on system clk, no holdover available
+      -
+        name: nco
+        doc: dpll driven by Numerically Controlled Oscillator
+    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: unspec
+        doc: unspecified value
+      -
+        name: unlocked
+        doc: |
+          dpll was not yet locked to any valid source (or is in one of
+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
+      -
+        name: calibrating
+        doc: dpll is trying to lock to a valid signal
+      -
+        name: locked
+        doc: dpll is locked
+      -
+        name: holdover
+        doc: |
+          dpll is in holdover state - lost a valid lock or was forced by
+          selecting DPLL_MODE_HOLDOVER mode
+    render-max: true
+  -
+    type: const
+    name: temp-divider
+    value: 10
+    doc: |
+      temperature divider allowing userspace to calculate the
+      temperature as float with single digit precision.
+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
+      tempearture 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: unspec
+        doc: unspecified value
+      -
+        name: pps
+        doc: dpll produces Pulse-Per-Second signal
+      -
+        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: unspec
+        doc: unspecified value
+      -
+        name: mux
+        doc: aggregates another layer of selectable pins
+      -
+        name: ext
+        doc: external source
+      -
+        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: unspec
+        doc: unspecified value
+      -
+        name: source
+        doc: pin used as a source of a signal
+      -
+        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-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: unspec
+        doc: unspecified value
+      -
+        name: connected
+        doc: pin connected, active source of phase locked loop
+      -
+        name: disconnected
+        doc: pin disconnected, not considered as a valid source
+      -
+        name: selectable
+        doc: pin enabled for automatic source 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
+  -
+    type: enum
+    name: event
+    doc: events of dpll generic netlink family
+    entries:
+      -
+        name: unspec
+        doc: invalid event type
+      -
+        name: device-create
+        doc: dpll device created
+      -
+        name: device-delete
+        doc: dpll device deleted
+      -
+        name: device-change
+        doc: |
+          attribute of dpll device or pin changed, reason is to be found with
+          an attribute type (DPLL_A_*) received with the event
+
+
+attribute-sets:
+  -
+    name: dpll
+    enum-name: dplla
+    attributes:
+      -
+        name: device
+        type: nest
+        value: 1
+        multi-attr: true
+        nested-attributes: device
+      -
+        name: id
+        type: u32
+      -
+        name: dev-name
+        type: string
+      -
+        name: bus-name
+        type: string
+      -
+        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: clock-id
+        type: u64
+      -
+        name: type
+        type: u8
+        enum: type
+      -
+        name: pin-idx
+        type: u32
+      -
+        name: pin-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-parent
+        type: nest
+        multi-attr: true
+        nested-attributes: pin-parent
+      -
+        name: pin-parent-idx
+        type: u32
+      -
+        name: pin-rclk-device
+        type: string
+      -
+        name: pin-dpll-caps
+        type: u32
+  -
+    name: device
+    subset-of: dpll
+    attributes:
+      -
+        name: id
+        type: u32
+        value: 2
+      -
+        name: dev-name
+        type: string
+      -
+        name: bus-name
+        type: string
+      -
+        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: clock-id
+        type: u64
+      -
+        name: type
+        type: u8
+        enum: type
+      -
+        name: pin-prio
+        type: u32
+        value: 19
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+  -
+    name: pin-parent
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-state
+        type: u8
+        value: 20
+        enum: pin-state
+      -
+        name: pin-parent-idx
+        type: u32
+        value: 22
+      -
+        name: pin-rclk-device
+        type: string
+  -
+    name: pin-frequency-range
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-frequency-min
+        type: u64
+        value: 17
+      -
+        name: pin-frequency-max
+        type: u64
+
+operations:
+  list:
+    -
+      name: unspec
+      doc: unused
+
+    -
+      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
+            - bus-name
+            - dev-name
+        reply:
+          attributes:
+            - device
+
+      dump:
+        pre: dpll-pre-dumpit
+        post: dpll-post-dumpit
+        reply:
+          attributes:
+            - device
+
+    -
+      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
+            - bus-name
+            - dev-name
+            - mode
+
+    -
+      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
+            - bus-name
+            - dev-name
+            - pin-idx
+        reply: &pin-attrs
+          attributes:
+            - pin-idx
+            - pin-label
+            - pin-type
+            - pin-direction
+            - pin-frequency
+            - pin-frequency-supported
+            - pin-parent
+            - pin-rclk-device
+            - pin-dpll-caps
+            - device
+
+      dump:
+        pre: dpll-pin-pre-dumpit
+        post: dpll-pin-post-dumpit
+        request:
+          attributes:
+            - id
+            - bus-name
+            - dev-name
+        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
+            - bus-name
+            - dev-name
+            - pin-idx
+            - pin-frequency
+            - pin-direction
+            - pin-prio
+            - pin-state
+            - pin-parent-idx
+
+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..2f8643f401b0
--- /dev/null
+++ b/drivers/dpll/dpll_nl.c
@@ -0,0 +1,126 @@
+// 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 <linux/dpll.h>
+
+/* DPLL_CMD_DEVICE_GET - do */
+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_MODE] = 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_IDX + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
+};
+
+/* DPLL_CMD_PIN_GET - dump */
+static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+};
+
+/* DPLL_CMD_PIN_SET - do */
+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
+	[DPLL_A_ID] = { .type = NLA_U32, },
+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
+};
+
+/* Ops table for dpll */
+static const struct genl_split_ops dpll_nl_ops[] = {
+	{
+		.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_BUS_NAME,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd	= DPLL_CMD_DEVICE_GET,
+		.start	= dpll_pre_dumpit,
+		.dumpit	= dpll_nl_device_get_dumpit,
+		.done	= dpll_post_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_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_IDX,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DPLL_CMD_PIN_GET,
+		.start		= dpll_pin_pre_dumpit,
+		.dumpit		= dpll_nl_pin_get_dumpit,
+		.done		= dpll_pin_post_dumpit,
+		.policy		= dpll_pin_get_dump_nl_policy,
+		.maxattr	= DPLL_A_BUS_NAME,
+		.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_IDX,
+		.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..57ab2da562ba
--- /dev/null
+++ b/drivers/dpll/dpll_nl.h
@@ -0,0 +1,42 @@
+/* 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 <linux/dpll.h>
+
+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_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_pre_dumpit(struct netlink_callback *cb);
+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
+int dpll_post_dumpit(struct netlink_callback *cb);
+int dpll_pin_post_dumpit(struct netlink_callback *cb);
+
+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_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..e188bc189754
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,202 @@
+/* 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 sources to syntonize with it, valid values for
+ *   DPLL_A_MODE attribute
+ * @DPLL_MODE_UNSPEC: unspecified value
+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to dpll
+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
+ */
+enum dpll_mode {
+	DPLL_MODE_UNSPEC,
+	DPLL_MODE_MANUAL,
+	DPLL_MODE_AUTOMATIC,
+	DPLL_MODE_HOLDOVER,
+	DPLL_MODE_FREERUN,
+	DPLL_MODE_NCO,
+
+	__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_UNSPEC: unspecified value
+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid source (or
+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid signal
+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid lock or
+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
+ */
+enum dpll_lock_status {
+	DPLL_LOCK_STATUS_UNSPEC,
+	DPLL_LOCK_STATUS_UNLOCKED,
+	DPLL_LOCK_STATUS_CALIBRATING,
+	DPLL_LOCK_STATUS_LOCKED,
+	DPLL_LOCK_STATUS_HOLDOVER,
+
+	__DPLL_LOCK_STATUS_MAX,
+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
+};
+
+#define DPLL_TEMP_DIVIDER	10
+
+/**
+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
+ * @DPLL_TYPE_UNSPEC: unspecified value
+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
+ */
+enum dpll_type {
+	DPLL_TYPE_UNSPEC,
+	DPLL_TYPE_PPS,
+	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_UNSPEC: unspecified value
+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
+ * @DPLL_PIN_TYPE_EXT: external source
+ * @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_UNSPEC,
+	DPLL_PIN_TYPE_MUX,
+	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_UNSPEC: unspecified value
+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
+ */
+enum dpll_pin_direction {
+	DPLL_PIN_DIRECTION_UNSPEC,
+	DPLL_PIN_DIRECTION_SOURCE,
+	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_MHZ	10000000
+
+/**
+ * enum dpll_pin_state - defines possible states of a pin, valid values for
+ *   DPLL_A_PIN_STATE attribute
+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase locked loop
+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
+ *   source
+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
+ */
+enum dpll_pin_state {
+	DPLL_PIN_STATE_UNSPEC,
+	DPLL_PIN_STATE_CONNECTED,
+	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_event - events of dpll generic netlink family
+ * @DPLL_EVENT_UNSPEC: invalid event type
+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed, reason
+ *   is to be found with an attribute type (DPLL_A_*) received with the event
+ */
+enum dpll_event {
+	DPLL_EVENT_UNSPEC,
+	DPLL_EVENT_DEVICE_CREATE,
+	DPLL_EVENT_DEVICE_DELETE,
+	DPLL_EVENT_DEVICE_CHANGE,
+};
+
+enum dplla {
+	DPLL_A_DEVICE = 1,
+	DPLL_A_ID,
+	DPLL_A_DEV_NAME,
+	DPLL_A_BUS_NAME,
+	DPLL_A_MODE,
+	DPLL_A_MODE_SUPPORTED,
+	DPLL_A_LOCK_STATUS,
+	DPLL_A_TEMP,
+	DPLL_A_CLOCK_ID,
+	DPLL_A_TYPE,
+	DPLL_A_PIN_IDX,
+	DPLL_A_PIN_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_PARENT,
+	DPLL_A_PIN_PARENT_IDX,
+	DPLL_A_PIN_RCLK_DEVICE,
+	DPLL_A_PIN_DPLL_CAPS,
+
+	__DPLL_A_MAX,
+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
+};
+
+enum {
+	DPLL_CMD_UNSPEC = 1,
+	DPLL_CMD_DEVICE_GET,
+	DPLL_CMD_DEVICE_SET,
+	DPLL_CMD_PIN_GET,
+	DPLL_CMD_PIN_SET,
+
+	__DPLL_CMD_MAX,
+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
+};
+
+#define DPLL_MCGRP_MONITOR	"monitor"
+
+#endif /* _UAPI_LINUX_DPLL_H */
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 MAINTAINERS                 |   8 +
 drivers/Kconfig             |   2 +
 drivers/Makefile            |   1 +
 drivers/dpll/Kconfig        |   7 +
 drivers/dpll/Makefile       |  10 +
 drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    | 113 +++++
 drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |  27 +
 include/linux/dpll.h        | 274 ++++++++++
 include/uapi/linux/dpll.h   |   2 +
 11 files changed, 2355 insertions(+)
 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 include/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index ebd26b3ca90e..710976c0737e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6302,6 +6302,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 <vadfed@fb.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/dpll/*
+F:	include/net/dpll.h
+F:	include/uapi/linux/dpll.h
+
 DRBD DRIVER
 M:	Philipp Reisner <philipp.reisner@linbit.com>
 M:	Lars Ellenberg <lars.ellenberg@linbit.com>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 968bd0a6fd78..453df9e1210d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
 
 source "drivers/hte/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 20b118dca999..9ffb554507ef 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
 obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
 obj-$(CONFIG_DRM_ACCEL)		+= accel/
+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..803bb5db7793
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,10 @@
+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..8a2370740026
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,939 @@
+// 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_xa_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
+ *
+ * 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_device_get_by_name - find dpll device by it's id
+ * @bus_name: bus name of searched dpll
+ * @dev_name: dev name of searched dpll
+ *
+ * Return:
+ * * dpll_device struct if found
+ * * NULL otherwise
+ */
+struct dpll_device *
+dpll_device_get_by_name(const char *bus_name, const char *device_name)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
+		    !strcmp(dev_name(&dpll->dev), device_name)) {
+			ret = dpll;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+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.
+ *
+ * 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
+ *
+ * Decrement refcount of existing pin reference on given xarray.
+ * If all registrations are lifted delete the reference and free its memory.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Decrement refcount of existing dpll reference on given xarray.
+ * If all references are dropped, delete the reference and free its memory.
+ */
+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_dplls: dpll_pin_ref xarray holding dplls
+ * @dpll: pointer to a dpll
+ *
+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
+ *
+ * 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;
+}
+
+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.
+ *
+ * Return:
+ * * dpll_device struct pointer if succeeded
+ * * ERR_PTR(X) - failed allocation
+ */
+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.
+ *
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR of an error
+ */
+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_xa_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_xa_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
+ *
+ * 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_xa_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_xa_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
+ * @owner: pointer to owner device
+ *
+ * Make dpll device available for user space.
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL on failure
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
+{
+	struct dpll_device_registration *reg;
+	bool first_registration = false;
+
+	if (WARN_ON(!ops || !owner))
+		return -EINVAL;
+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+
+	mutex_lock(&dpll_xa_lock);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
+		mutex_unlock(&dpll_xa_lock);
+		return -EEXIST;
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_xa_lock);
+		return -EEXIST;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+
+	dpll->dev.bus = owner->bus;
+	dpll->parent = owner;
+	dpll->type = type;
+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
+		     dpll->clock_id, dpll->device_idx);
+
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_xa_lock);
+		return 0;
+	}
+
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_xa_lock);
+	dpll_notify_device_create(dpll);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+/**
+ * dpll_device_unregister - deregister dpll device
+ * @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Deregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	mutex_lock(&dpll_xa_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_xa_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_xa_lock);
+		return;
+	}
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_xa_lock);
+	dpll_notify_device_delete(dpll);
+}
+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
+ *
+ * Return:
+ * valid allocated dpll_pin struct pointer if succeeded
+ * ERR_PTR of an error
+ */
+static struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret, fs_size;
+
+	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;
+	refcount_set(&pin->refcount, 1);
+	if (WARN_ON(!prop->label)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
+	if (!pin->prop.label) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop.type = prop->type;
+	pin->prop.capabilities = prop->capabilities;
+	if (prop->freq_supported_num) {
+		fs_size = sizeof(*pin->prop.freq_supported) *
+			  prop->freq_supported_num;
+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
+		if (!pin->prop.freq_supported) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
+		pin->prop.freq_supported_num = prop->freq_supported_num;
+	}
+	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->prop.label);
+	kfree(pin->rclk_dev_name);
+	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.
+ *
+ * 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
+ * @dpll: dpll_device struct pointer
+ *
+ * Drop reference for a pin, if all references are gone, delete pin object.
+ */
+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->prop.label);
+		kfree(pin->prop.freq_supported);
+		kfree(pin->rclk_dev_name);
+		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,
+		    const char *rclk_device_name)
+{
+	int ret;
+
+	if (WARN_ON(!ops))
+		return -EINVAL;
+
+	if (rclk_device_name && !pin->rclk_dev_name) {
+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
+		if (!pin->rclk_dev_name)
+			return -ENOMEM;
+	}
+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
+	if (ret)
+		goto rclk_free;
+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
+	if (ret)
+		goto ref_pin_del;
+	else
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
+
+	return ret;
+
+ref_pin_del:
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+rclk_free:
+	kfree(pin->rclk_dev_name);
+	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
+ * @rclk_device: pointer to recovered clock device
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL - missing dpll or pin
+ * * -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,
+		  struct device *rclk_device)
+{
+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
+	int ret;
+
+	mutex_lock(&dpll_xa_lock);
+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
+	mutex_unlock(&dpll_xa_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 - deregister 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
+ */
+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_xa_lock);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_xa_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
+ * @rclk_device: pointer to recovered clock device
+ *
+ * Register a pin with a parent pin, create references between them and
+ * between newly registered pin and dplls connected with a parent pin.
+ *
+ * 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 device *rclk_device)
+{
+	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_xa_lock);
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
+					  rclk_device ?
+					  dev_name(rclk_device) : NULL);
+		mutex_unlock(&dpll_xa_lock);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
+	}
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref) {
+		if (i < stop) {
+			mutex_lock(&dpll_xa_lock);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+			mutex_unlock(&dpll_xa_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 - deregister 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
+ *
+ * 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_xa_lock);
+	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);
+		dpll_pin_parent_notify(ref->dpll, pin, parent,
+				       DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_priv(const struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
+	return reg->priv;
+}
+
+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;
+}
+
+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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load((struct xarray *)&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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
+			   const struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+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;
+}
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	return 0;
+
+error:
+	mutex_destroy(&dpll_xa_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..e905c1088568
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
+ * @id:			unique id number for each device
+ * @dev_driver_id:	id given by dev driver
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @module:		module of creator
+ * @dev:		struct device for this dpll device
+ * @parent:		parent device
+ * @ops:		operations this &dpll_device supports
+ * @lock:		mutex to serialize operations
+ * @type:		type of a dpll
+ * @pins:		list of pointers to pins registered with this dpll
+ * @mode_supported_mask: mask of supported modes
+ * @refcount:		refcount
+ * @priv:		pointer to private information of owner
+ **/
+struct dpll_device {
+	u32 id;
+	u32 device_idx;
+	u64 clock_id;
+	struct module *module;
+	struct device dev;
+	struct device *parent;
+	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
+ * @idx:		unique idx given by alloc on global pin's XA
+ * @dev_driver_id:	id given by dev driver
+ * @clock_id:		clock_id of creator
+ * @module:		module of creator
+ * @dpll_refs:		hold referencees to dplls that pin is registered with
+ * @pin_refs:		hold references to pins that pin is registered with
+ * @prop:		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;
+	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(const struct dpll_device *dpll);
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin);
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
+			   const 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);
+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
+					    const char *dev_name);
+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_xa_lock;
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..1eb0b4a2fce4
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,972 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ * Copyright (c) 2021 Meta Platforms, Inc. 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>
+
+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;
+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))
+		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, const struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref,
+		      struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const struct dpll_pin *pin,
+			       struct dpll_pin_ref *ref,
+			       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const struct dpll_pin *pin,
+			   struct dpll_pin_ref *ref,
+			   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const 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);
+	const 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 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);
+
+		ppin = ref->pin;
+
+		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, &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_PARENT_IDX, ppin->pin_idx)) {
+			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_DEVICE);
+		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;
+		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)
+{
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
+		return -EMSGSIZE;
+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	if (pin->rclk_dev_name)
+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
+				   pin->rclk_dev_name))
+			return -EMSGSIZE;
+	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, 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;
+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		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;
+
+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+					 dpll, dpll_priv(dpll), freq, extack);
+		if (ret)
+			return -EFAULT;
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
+			  u32 parent_idx, enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *pin_ref, *parent_ref;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
+	       //	dpll_pin_get_by_idx(dpll, parent_idx);
+	if (!parent_ref)
+		return -EINVAL;
+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
+	if (!pin_ref)
+		return -EINVAL;
+	ops = dpll_pin_ops(pin_ref);
+	if (!ops->state_on_pin_set)
+		return -EOPNOTSUPP;
+	if (ops->state_on_pin_set(pin_ref->pin,
+				  dpll_pin_on_pin_priv(parent_ref->pin,
+						       pin_ref->pin),
+				  parent_ref->pin, state, extack))
+		return -EFAULT;
+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
+			       DPLL_A_PIN_STATE);
+
+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
+
+	return 0;
+}
+
+static int
+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	u32 prio = nla_get_u8(prio_attr);
+
+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
+
+	return 0;
+}
+
+static int
+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
+		       struct netlink_ext_ack *extack)
+{
+	enum dpll_pin_direction direction = nla_get_u8(a);
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+
+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				       dpll, dpll_priv(dpll), direction,
+				       extack))
+			return -EFAULT;
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct genl_info *info)
+{
+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
+	bool parent_present = false;
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+	u32 parent_idx;
+
+	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_DIRECTION:
+			ret = dpll_pin_direction_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PRIO:
+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_IDX:
+			parent_present = true;
+			parent_idx = nla_get_u32(a);
+			break;
+		case DPLL_A_PIN_STATE:
+			state = nla_get_u8(a);
+			break;
+		default:
+			break;
+		}
+	}
+	if (state != DPLL_PIN_STATE_UNSPEC) {
+		if (!parent_present) {
+			ret = dpll_pin_state_set(dpll, pin, state,
+						 info->extack);
+			if (ret)
+				return ret;
+		} else {
+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
+							state, info->extack);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct dpll_pin *pin = info->user_ptr[1];
+
+	return dpll_pin_set_from_nlattr(dpll, pin, info);
+}
+
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[1];
+	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;
+}
+
+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 *attr;
+	enum dpll_mode mode;
+	int rem, ret = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_MODE:
+			mode = nla_get_u8(attr);
+
+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
+					    info->extack);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+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_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_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_xa_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_xa_lock);
+	return -ENODEV;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_xa_lock);
+}
+
+int dpll_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_xa_lock);
+
+	return 0;
+}
+
+int dpll_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_xa_lock);
+
+	return 0;
+}
+
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		      struct genl_info *info)
+{
+	int ret = dpll_pre_doit(ops, skb, info);
+	struct dpll_pin_ref *pin_ref;
+	struct dpll_device *dpll;
+
+	if (ret)
+		return ret;
+	dpll = info->user_ptr[0];
+	if (!info->attrs[DPLL_A_PIN_IDX]) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	pin_ref = xa_load(&dpll->pin_refs,
+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
+	if (!pin_ref) {
+		ret = -ENODEV;
+		goto unlock_dev;
+	}
+	info->user_ptr[1] = pin_ref->pin;
+
+	return 0;
+
+unlock_dev:
+	mutex_unlock(&dpll_xa_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	dpll_post_doit(ops, skb, info);
+}
+
+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
+{
+	return dpll_pre_dumpit(cb);
+}
+
+int dpll_pin_post_dumpit(struct netlink_callback *cb)
+{
+	return dpll_post_dumpit(cb);
+}
+
+static int
+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct dpll_pin *parent,
+			 enum dplla attr)
+{
+	int ret = dpll_msg_add_dev_handle(msg, dpll);
+	struct dpll_pin_ref *ref = NULL;
+	enum dpll_pin_state state;
+
+	if (ret)
+		return ret;
+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		return -EMSGSIZE;
+
+	switch (attr) {
+	case DPLL_A_MODE:
+		ret = dpll_msg_add_mode(msg, dpll, NULL);
+		break;
+	case DPLL_A_LOCK_STATUS:
+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
+		break;
+	case DPLL_A_TEMP:
+		ret = dpll_msg_add_temp(msg, dpll, NULL);
+		break;
+	case DPLL_A_PIN_FREQUENCY:
+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
+		break;
+	case DPLL_A_PIN_PRIO:
+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
+		break;
+	case DPLL_A_PIN_STATE:
+		if (parent) {
+			const struct dpll_pin_ops *ops;
+			void *priv = dpll_pin_on_pin_priv(parent, pin);
+
+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
+			if (!ref)
+				return -EFAULT;
+			ops = dpll_pin_ops(ref);
+			if (!ops->state_on_pin_get)
+				return -EOPNOTSUPP;
+			ret = ops->state_on_pin_get(pin, priv, parent,
+						    &state, NULL);
+			if (ret)
+				return ret;
+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
+					parent->pin_idx))
+				return -EMSGSIZE;
+		} else {
+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+			if (!ref)
+				return -EFAULT;
+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
+							     NULL);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static int
+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	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_msg_add_dev_handle(msg, dpll);
+	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;
+}
+
+static int
+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
+		       struct dpll_pin *parent, enum dplla attr)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
+			  DPLL_EVENT_DEVICE_CHANGE);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
+	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_notify_device_create(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
+}
+
+int dpll_notify_device_delete(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
+}
+
+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
+{
+	if (WARN_ON(!dpll))
+		return -EINVAL;
+
+	return dpll_send_event_change(dpll, NULL, NULL, attr);
+}
+EXPORT_SYMBOL_GPL(dpll_device_notify);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr)
+{
+	return dpll_send_event_change(dpll, pin, NULL, attr);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_notify);
+
+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+			   struct dpll_pin *parent, enum dplla attr)
+{
+	return dpll_send_event_change(dpll, pin, parent, attr);
+}
+
+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..952e0335595e
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+/**
+ * dpll_notify_device_create - notify that the device has been created
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_create(struct dpll_device *dpll);
+
+
+/**
+ * dpll_notify_device_delete - notify that the device has been deleted
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_delete(struct dpll_device *dpll);
+
+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+			   struct dpll_pin *parent, enum dplla attr);
+
+int __init dpll_netlink_init(void);
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..5194efaf55a8
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,274 @@
+/* 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,
+				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,
+				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 *label;
+	enum dpll_pin_type type;
+	unsigned long capabilities;
+	u32 freq_supported_num;
+	struct dpll_pin_frequency *freq_supported;
+};
+
+/**
+ * dpll_device_get - find or create dpll_device object
+ * @clock_id: a system unique number for a device
+ * @dev_driver_id: index of dpll device on parent device
+ * @module: register module
+ *
+ * Returns:
+ * * pointer to initialized dpll - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_device
+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+
+/**
+ * dpll_device_put - caller drops reference to the device, free resources
+ * @dpll: dpll device pointer
+ *
+ * If all dpll_device_get callers drops their reference, the dpll device
+ * resources are freed.
+ */
+void dpll_device_put(struct dpll_device *dpll);
+
+/**
+ * dpll_device_register - register device, make it visible in the subsystem.
+ * @dpll: reference previously allocated with dpll_device_get
+ * @type: type of dpll
+ * @ops: callbacks
+ * @priv: private data of registerer
+ * @owner: device struct of the owner
+ *
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
+
+/**
+ * dpll_device_unregister - deregister registered dpll
+ * @dpll: pointer to dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
+
+/**
+ * dpll_pin_get - get reference or create new pin object
+ * @clock_id: a system unique number of a device
+ * @@dev_driver_id: index of dpll device on parent device
+ * @module: register module
+ * @pin_prop: constant properities of a pin
+ *
+ * find existing pin with given clock_id, @dev_driver_id and module, or create new
+ * and returen its reference.
+ *
+ * Returns:
+ * * pointer to initialized pin - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_pin
+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+	      const struct dpll_pin_properties *prop);
+
+/**
+ * dpll_pin_register - register pin with a dpll device
+ * @dpll: pointer to dpll object to register pin with
+ * @pin: pointer to allocated pin object being registered with dpll
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * Register previously allocated pin object with a dpll device.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this dpll,
+ * * -EBUSY - couldn't allocate id for a pin.
+ */
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv,
+		      struct device *rclk_device);
+
+/**
+ * dpll_pin_unregister - deregister pin from a dpll device
+ * @dpll: pointer to dpll object to deregister pin from
+ * @pin: pointer to allocated pin object being deregistered from dpll
+ * @ops: ops for a dpll pin ops
+ * @priv: pointer to private information of owner
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ */
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
+ * @pin: pointer to allocated pin
+ *
+ * Pins shall be deregistered from all dpll devices before putting them,
+ * otherwise the memory won't be freed.
+ */
+void dpll_pin_put(struct dpll_pin *pin);
+
+/**
+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
+ * @parent: parent pin pointer
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ */
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv,
+			     struct device *rclk_device);
+
+/**
+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
+ * @parent: parent pin pointer
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_device_notify - notify on dpll device change
+ * @dpll: dpll device pointer
+ * @attr: changed attribute
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr);
+
+
+
+#endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
index e188bc189754..75eeaa4396eb 100644
--- a/include/uapi/linux/dpll.h
+++ b/include/uapi/linux/dpll.h
@@ -111,6 +111,8 @@ enum dpll_pin_direction {
 
 #define DPLL_PIN_FREQUENCY_1_HZ		1
 #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
 
 /**
  * enum dpll_pin_state - defines possible states of a pin, valid values for
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 MAINTAINERS                 |   8 +
 drivers/Kconfig             |   2 +
 drivers/Makefile            |   1 +
 drivers/dpll/Kconfig        |   7 +
 drivers/dpll/Makefile       |  10 +
 drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    | 113 +++++
 drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |  27 +
 include/linux/dpll.h        | 274 ++++++++++
 include/uapi/linux/dpll.h   |   2 +
 11 files changed, 2355 insertions(+)
 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 include/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index ebd26b3ca90e..710976c0737e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6302,6 +6302,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 <vadfed@fb.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/dpll/*
+F:	include/net/dpll.h
+F:	include/uapi/linux/dpll.h
+
 DRBD DRIVER
 M:	Philipp Reisner <philipp.reisner@linbit.com>
 M:	Lars Ellenberg <lars.ellenberg@linbit.com>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 968bd0a6fd78..453df9e1210d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
 
 source "drivers/hte/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 20b118dca999..9ffb554507ef 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
 obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
 obj-$(CONFIG_DRM_ACCEL)		+= accel/
+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..803bb5db7793
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,10 @@
+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..8a2370740026
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,939 @@
+// 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_xa_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
+ *
+ * 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_device_get_by_name - find dpll device by it's id
+ * @bus_name: bus name of searched dpll
+ * @dev_name: dev name of searched dpll
+ *
+ * Return:
+ * * dpll_device struct if found
+ * * NULL otherwise
+ */
+struct dpll_device *
+dpll_device_get_by_name(const char *bus_name, const char *device_name)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
+		    !strcmp(dev_name(&dpll->dev), device_name)) {
+			ret = dpll;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+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.
+ *
+ * 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
+ *
+ * Decrement refcount of existing pin reference on given xarray.
+ * If all registrations are lifted delete the reference and free its memory.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Decrement refcount of existing dpll reference on given xarray.
+ * If all references are dropped, delete the reference and free its memory.
+ */
+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_dplls: dpll_pin_ref xarray holding dplls
+ * @dpll: pointer to a dpll
+ *
+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
+ *
+ * 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;
+}
+
+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.
+ *
+ * Return:
+ * * dpll_device struct pointer if succeeded
+ * * ERR_PTR(X) - failed allocation
+ */
+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.
+ *
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR of an error
+ */
+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_xa_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_xa_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
+ *
+ * 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_xa_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_xa_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
+ * @owner: pointer to owner device
+ *
+ * Make dpll device available for user space.
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL on failure
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
+{
+	struct dpll_device_registration *reg;
+	bool first_registration = false;
+
+	if (WARN_ON(!ops || !owner))
+		return -EINVAL;
+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+
+	mutex_lock(&dpll_xa_lock);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
+		mutex_unlock(&dpll_xa_lock);
+		return -EEXIST;
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_xa_lock);
+		return -EEXIST;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+
+	dpll->dev.bus = owner->bus;
+	dpll->parent = owner;
+	dpll->type = type;
+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
+		     dpll->clock_id, dpll->device_idx);
+
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_xa_lock);
+		return 0;
+	}
+
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_xa_lock);
+	dpll_notify_device_create(dpll);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+/**
+ * dpll_device_unregister - deregister dpll device
+ * @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Deregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	mutex_lock(&dpll_xa_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_xa_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_xa_lock);
+		return;
+	}
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_xa_lock);
+	dpll_notify_device_delete(dpll);
+}
+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
+ *
+ * Return:
+ * valid allocated dpll_pin struct pointer if succeeded
+ * ERR_PTR of an error
+ */
+static struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret, fs_size;
+
+	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;
+	refcount_set(&pin->refcount, 1);
+	if (WARN_ON(!prop->label)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
+	if (!pin->prop.label) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop.type = prop->type;
+	pin->prop.capabilities = prop->capabilities;
+	if (prop->freq_supported_num) {
+		fs_size = sizeof(*pin->prop.freq_supported) *
+			  prop->freq_supported_num;
+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
+		if (!pin->prop.freq_supported) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
+		pin->prop.freq_supported_num = prop->freq_supported_num;
+	}
+	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->prop.label);
+	kfree(pin->rclk_dev_name);
+	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.
+ *
+ * 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
+ * @dpll: dpll_device struct pointer
+ *
+ * Drop reference for a pin, if all references are gone, delete pin object.
+ */
+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->prop.label);
+		kfree(pin->prop.freq_supported);
+		kfree(pin->rclk_dev_name);
+		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,
+		    const char *rclk_device_name)
+{
+	int ret;
+
+	if (WARN_ON(!ops))
+		return -EINVAL;
+
+	if (rclk_device_name && !pin->rclk_dev_name) {
+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
+		if (!pin->rclk_dev_name)
+			return -ENOMEM;
+	}
+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
+	if (ret)
+		goto rclk_free;
+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
+	if (ret)
+		goto ref_pin_del;
+	else
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
+
+	return ret;
+
+ref_pin_del:
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+rclk_free:
+	kfree(pin->rclk_dev_name);
+	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
+ * @rclk_device: pointer to recovered clock device
+ *
+ * Return:
+ * * 0 on success
+ * * -EINVAL - missing dpll or pin
+ * * -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,
+		  struct device *rclk_device)
+{
+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
+	int ret;
+
+	mutex_lock(&dpll_xa_lock);
+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
+	mutex_unlock(&dpll_xa_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 - deregister 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
+ */
+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_xa_lock);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_xa_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
+ * @rclk_device: pointer to recovered clock device
+ *
+ * Register a pin with a parent pin, create references between them and
+ * between newly registered pin and dplls connected with a parent pin.
+ *
+ * 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 device *rclk_device)
+{
+	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_xa_lock);
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
+					  rclk_device ?
+					  dev_name(rclk_device) : NULL);
+		mutex_unlock(&dpll_xa_lock);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
+	}
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref) {
+		if (i < stop) {
+			mutex_lock(&dpll_xa_lock);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+			mutex_unlock(&dpll_xa_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 - deregister 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
+ *
+ * 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_xa_lock);
+	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);
+		dpll_pin_parent_notify(ref->dpll, pin, parent,
+				       DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_priv(const struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
+	return reg->priv;
+}
+
+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;
+}
+
+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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load((struct xarray *)&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
+ *
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
+			   const struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+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;
+}
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	return 0;
+
+error:
+	mutex_destroy(&dpll_xa_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..e905c1088568
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
+ * @id:			unique id number for each device
+ * @dev_driver_id:	id given by dev driver
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @module:		module of creator
+ * @dev:		struct device for this dpll device
+ * @parent:		parent device
+ * @ops:		operations this &dpll_device supports
+ * @lock:		mutex to serialize operations
+ * @type:		type of a dpll
+ * @pins:		list of pointers to pins registered with this dpll
+ * @mode_supported_mask: mask of supported modes
+ * @refcount:		refcount
+ * @priv:		pointer to private information of owner
+ **/
+struct dpll_device {
+	u32 id;
+	u32 device_idx;
+	u64 clock_id;
+	struct module *module;
+	struct device dev;
+	struct device *parent;
+	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
+ * @idx:		unique idx given by alloc on global pin's XA
+ * @dev_driver_id:	id given by dev driver
+ * @clock_id:		clock_id of creator
+ * @module:		module of creator
+ * @dpll_refs:		hold referencees to dplls that pin is registered with
+ * @pin_refs:		hold references to pins that pin is registered with
+ * @prop:		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;
+	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(const struct dpll_device *dpll);
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin);
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
+			   const 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);
+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
+					    const char *dev_name);
+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_xa_lock;
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..1eb0b4a2fce4
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,972 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ * Copyright (c) 2021 Meta Platforms, Inc. 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>
+
+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;
+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))
+		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, const struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref,
+		      struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const struct dpll_pin *pin,
+			       struct dpll_pin_ref *ref,
+			       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const struct dpll_pin *pin,
+			   struct dpll_pin_ref *ref,
+			   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	const 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, const 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);
+	const 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 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);
+
+		ppin = ref->pin;
+
+		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, &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_PARENT_IDX, ppin->pin_idx)) {
+			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_DEVICE);
+		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;
+		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)
+{
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
+		return -EMSGSIZE;
+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	if (pin->rclk_dev_name)
+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
+				   pin->rclk_dev_name))
+			return -EMSGSIZE;
+	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, 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;
+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		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;
+
+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+					 dpll, dpll_priv(dpll), freq, extack);
+		if (ret)
+			return -EFAULT;
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
+			  u32 parent_idx, enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *pin_ref, *parent_ref;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
+	       //	dpll_pin_get_by_idx(dpll, parent_idx);
+	if (!parent_ref)
+		return -EINVAL;
+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
+	if (!pin_ref)
+		return -EINVAL;
+	ops = dpll_pin_ops(pin_ref);
+	if (!ops->state_on_pin_set)
+		return -EOPNOTSUPP;
+	if (ops->state_on_pin_set(pin_ref->pin,
+				  dpll_pin_on_pin_priv(parent_ref->pin,
+						       pin_ref->pin),
+				  parent_ref->pin, state, extack))
+		return -EFAULT;
+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
+			       DPLL_A_PIN_STATE);
+
+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
+
+	return 0;
+}
+
+static int
+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	u32 prio = nla_get_u8(prio_attr);
+
+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
+
+	return 0;
+}
+
+static int
+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
+		       struct netlink_ext_ack *extack)
+{
+	enum dpll_pin_direction direction = nla_get_u8(a);
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+
+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				       dpll, dpll_priv(dpll), direction,
+				       extack))
+			return -EFAULT;
+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct genl_info *info)
+{
+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
+	bool parent_present = false;
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+	u32 parent_idx;
+
+	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_DIRECTION:
+			ret = dpll_pin_direction_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PRIO:
+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_IDX:
+			parent_present = true;
+			parent_idx = nla_get_u32(a);
+			break;
+		case DPLL_A_PIN_STATE:
+			state = nla_get_u8(a);
+			break;
+		default:
+			break;
+		}
+	}
+	if (state != DPLL_PIN_STATE_UNSPEC) {
+		if (!parent_present) {
+			ret = dpll_pin_state_set(dpll, pin, state,
+						 info->extack);
+			if (ret)
+				return ret;
+		} else {
+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
+							state, info->extack);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct dpll_pin *pin = info->user_ptr[1];
+
+	return dpll_pin_set_from_nlattr(dpll, pin, info);
+}
+
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[1];
+	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;
+}
+
+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 *attr;
+	enum dpll_mode mode;
+	int rem, ret = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_MODE:
+			mode = nla_get_u8(attr);
+
+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
+					    info->extack);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+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_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_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_xa_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_xa_lock);
+	return -ENODEV;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_xa_lock);
+}
+
+int dpll_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_xa_lock);
+
+	return 0;
+}
+
+int dpll_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_xa_lock);
+
+	return 0;
+}
+
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		      struct genl_info *info)
+{
+	int ret = dpll_pre_doit(ops, skb, info);
+	struct dpll_pin_ref *pin_ref;
+	struct dpll_device *dpll;
+
+	if (ret)
+		return ret;
+	dpll = info->user_ptr[0];
+	if (!info->attrs[DPLL_A_PIN_IDX]) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	pin_ref = xa_load(&dpll->pin_refs,
+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
+	if (!pin_ref) {
+		ret = -ENODEV;
+		goto unlock_dev;
+	}
+	info->user_ptr[1] = pin_ref->pin;
+
+	return 0;
+
+unlock_dev:
+	mutex_unlock(&dpll_xa_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	dpll_post_doit(ops, skb, info);
+}
+
+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
+{
+	return dpll_pre_dumpit(cb);
+}
+
+int dpll_pin_post_dumpit(struct netlink_callback *cb)
+{
+	return dpll_post_dumpit(cb);
+}
+
+static int
+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct dpll_pin *parent,
+			 enum dplla attr)
+{
+	int ret = dpll_msg_add_dev_handle(msg, dpll);
+	struct dpll_pin_ref *ref = NULL;
+	enum dpll_pin_state state;
+
+	if (ret)
+		return ret;
+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		return -EMSGSIZE;
+
+	switch (attr) {
+	case DPLL_A_MODE:
+		ret = dpll_msg_add_mode(msg, dpll, NULL);
+		break;
+	case DPLL_A_LOCK_STATUS:
+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
+		break;
+	case DPLL_A_TEMP:
+		ret = dpll_msg_add_temp(msg, dpll, NULL);
+		break;
+	case DPLL_A_PIN_FREQUENCY:
+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
+		break;
+	case DPLL_A_PIN_PRIO:
+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
+		break;
+	case DPLL_A_PIN_STATE:
+		if (parent) {
+			const struct dpll_pin_ops *ops;
+			void *priv = dpll_pin_on_pin_priv(parent, pin);
+
+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
+			if (!ref)
+				return -EFAULT;
+			ops = dpll_pin_ops(ref);
+			if (!ops->state_on_pin_get)
+				return -EOPNOTSUPP;
+			ret = ops->state_on_pin_get(pin, priv, parent,
+						    &state, NULL);
+			if (ret)
+				return ret;
+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
+					parent->pin_idx))
+				return -EMSGSIZE;
+		} else {
+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+			if (!ref)
+				return -EFAULT;
+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
+							     NULL);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static int
+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	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_msg_add_dev_handle(msg, dpll);
+	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;
+}
+
+static int
+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
+		       struct dpll_pin *parent, enum dplla attr)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
+			  DPLL_EVENT_DEVICE_CHANGE);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
+	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_notify_device_create(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
+}
+
+int dpll_notify_device_delete(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
+}
+
+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
+{
+	if (WARN_ON(!dpll))
+		return -EINVAL;
+
+	return dpll_send_event_change(dpll, NULL, NULL, attr);
+}
+EXPORT_SYMBOL_GPL(dpll_device_notify);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr)
+{
+	return dpll_send_event_change(dpll, pin, NULL, attr);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_notify);
+
+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+			   struct dpll_pin *parent, enum dplla attr)
+{
+	return dpll_send_event_change(dpll, pin, parent, attr);
+}
+
+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..952e0335595e
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+/**
+ * dpll_notify_device_create - notify that the device has been created
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_create(struct dpll_device *dpll);
+
+
+/**
+ * dpll_notify_device_delete - notify that the device has been deleted
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_delete(struct dpll_device *dpll);
+
+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+			   struct dpll_pin *parent, enum dplla attr);
+
+int __init dpll_netlink_init(void);
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..5194efaf55a8
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,274 @@
+/* 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,
+				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,
+				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 *label;
+	enum dpll_pin_type type;
+	unsigned long capabilities;
+	u32 freq_supported_num;
+	struct dpll_pin_frequency *freq_supported;
+};
+
+/**
+ * dpll_device_get - find or create dpll_device object
+ * @clock_id: a system unique number for a device
+ * @dev_driver_id: index of dpll device on parent device
+ * @module: register module
+ *
+ * Returns:
+ * * pointer to initialized dpll - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_device
+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+
+/**
+ * dpll_device_put - caller drops reference to the device, free resources
+ * @dpll: dpll device pointer
+ *
+ * If all dpll_device_get callers drops their reference, the dpll device
+ * resources are freed.
+ */
+void dpll_device_put(struct dpll_device *dpll);
+
+/**
+ * dpll_device_register - register device, make it visible in the subsystem.
+ * @dpll: reference previously allocated with dpll_device_get
+ * @type: type of dpll
+ * @ops: callbacks
+ * @priv: private data of registerer
+ * @owner: device struct of the owner
+ *
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
+
+/**
+ * dpll_device_unregister - deregister registered dpll
+ * @dpll: pointer to dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
+
+/**
+ * dpll_pin_get - get reference or create new pin object
+ * @clock_id: a system unique number of a device
+ * @@dev_driver_id: index of dpll device on parent device
+ * @module: register module
+ * @pin_prop: constant properities of a pin
+ *
+ * find existing pin with given clock_id, @dev_driver_id and module, or create new
+ * and returen its reference.
+ *
+ * Returns:
+ * * pointer to initialized pin - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_pin
+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+	      const struct dpll_pin_properties *prop);
+
+/**
+ * dpll_pin_register - register pin with a dpll device
+ * @dpll: pointer to dpll object to register pin with
+ * @pin: pointer to allocated pin object being registered with dpll
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * Register previously allocated pin object with a dpll device.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this dpll,
+ * * -EBUSY - couldn't allocate id for a pin.
+ */
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv,
+		      struct device *rclk_device);
+
+/**
+ * dpll_pin_unregister - deregister pin from a dpll device
+ * @dpll: pointer to dpll object to deregister pin from
+ * @pin: pointer to allocated pin object being deregistered from dpll
+ * @ops: ops for a dpll pin ops
+ * @priv: pointer to private information of owner
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ */
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
+ * @pin: pointer to allocated pin
+ *
+ * Pins shall be deregistered from all dpll devices before putting them,
+ * otherwise the memory won't be freed.
+ */
+void dpll_pin_put(struct dpll_pin *pin);
+
+/**
+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
+ * @parent: parent pin pointer
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ */
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv,
+			     struct device *rclk_device);
+
+/**
+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
+ * @parent: parent pin pointer
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
+ * from that device
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_device_notify - notify on dpll device change
+ * @dpll: dpll device pointer
+ * @attr: changed attribute
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr);
+
+
+
+#endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
index e188bc189754..75eeaa4396eb 100644
--- a/include/uapi/linux/dpll.h
+++ b/include/uapi/linux/dpll.h
@@ -111,6 +111,8 @@ enum dpll_pin_direction {
 
 #define DPLL_PIN_FREQUENCY_1_HZ		1
 #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
 
 /**
  * enum dpll_pin_state - defines possible states of a pin, valid values for
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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 <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
 Documentation/dpll.rst             | 408 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 409 insertions(+)
 create mode 100644 Documentation/dpll.rst

diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
new file mode 100644
index 000000000000..fba5bc027967
--- /dev/null
+++ b/Documentation/dpll.rst
@@ -0,0 +1,408 @@
+.. 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
+pins connected with it.
+It provides its supported working modes 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 same command.
+Requesting configuration of dpll device is done with `do` request of
+netlink ``DPLL_CMD_DEVICE_SET`` command.
+
+
+Pin object
+==========
+A pin is amorphic object which represents either source or output, it
+could be internal component of the device, as well as externaly
+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 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 manualy 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
+  source for automatic selection algorithm
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
+  a valid source 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 sources.
+
+
+Shared pins
+===========
+A single pin object can be registered 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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
+2) Set on a pin-dpll tuple - the configuration affects only selected
+   dpll device. (i.e. PIN_PRIO, PIN_STATE).
+
+
+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': [{
+        'device': [{'bus-name': 'pci',
+                    'dev-name': '0000:21:00.0_0', 'id': 0}],
+        'pin-direction': {'doc': 'pin used as a source of a signal',
+                          'name': 'source'},
+        'pin-idx': 13,
+        'pin-parent': [{'pin-parent-idx': 2,
+                        'pin-state': {'doc': 'pin disconnected',
+                                      'name': 'disconnected'}},
+                       {'pin-parent-idx': 3,
+                        'pin-state': {'doc': 'pin disconnected',
+                                      'name': 'disconnected'}}],
+        }]``
+
+Only one child pin can provide it's signal to the parent MUX-type pin at
+a time, the selection is done with requesting change of child pin state
+to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
+index value in ``DPLL_A_PARENT_PIN_IDX``.
+
+Pin priority
+============
+Some devices might offer a capability of automatic pin selection mode
+(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
+Usually such automatic selection is offloaded to the hardware,
+which means only pins directly connected to the dpll are capable of
+automatic source pin selection.
+In automatic selection mode, the user cannot manually select a source
+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 connect with it.
+Child pin of MUX-type is not capable of automatic source pin selection,
+in order to configure a source of a MUX-type pin, the user needs to
+request desired pin state of the child pin on the parent - it is done
+with providing additional attribute for pin set state request - index
+of parent pin he wish to propagate its signal to
+(``DPLL_A_PARENT_PIN_IDX``).
+
+
+Configuration commands group
+============================
+
+Configuration commands are used to get or dump information about
+registered dpll devices (and pins), as well as set configuration of
+device or pins. As dpll device could not be abstract and reflects real
+hardware, there is no way to add new dpll device via netlink from user
+space and each device should be registered by it's driver.
+
+All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
+any spamming/D.o.S. from unauthorized userspace applications.
+
+List of netlink commands with possible attributes
+=================================================
+
+All constants identifying command types use ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use ``DPLL_A_``
+prefix and suffix according to attribute purpose:
+
+  ============================  =======================================
+  ``DEVICE_GET``                command to get device info or dump list
+                                of available devices
+    ``ID``                      attr internal dpll device ID
+    ``DEV_NAME``                attr dpll device name
+    ``BUS_NAME``                attr dpll device bus name
+    ``MODE``                    attr selection mode
+    ``MODE_SUPPORTED``          attr available selection modes
+    ``LOCK_STATUS``             attr internal frequency-lock status
+    ``TEMP``                    attr device temperature information
+    ``CLOCK_ID``                attr Unique Clock Identifier (EUI-64),
+                                as defined by the IEEE 1588 standard
+    ``TYPE``                    attr type or purpose of dpll device
+  ``DEVICE_SET``                command to set dpll device configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``MODE``                    attr selection mode to configure
+  ``PIN_GET``                   command to get pin info or dump list of
+                                available pins
+    ``DEVICE``                  nest attr for each dpll device pin is
+                                connected with
+      ``ID``                    attr internal dpll device ID
+      ``DEV_NAME``              attr dpll device name
+      ``BUS_NAME``              attr dpll device bus name
+      ``PIN_PRIO``              attr priority of pin on the dpll device
+      ``PIN_STATE``             attr state of pin on the dpll device
+    ``PIN_IDX``                 attr index of a pin on the dpll device
+    ``PIN_DESCRIPTION``         attr description provided by driver
+    ``PIN_TYPE``                attr type of a pin
+    ``PIN_DIRECTION``           attr direction of a pin
+    ``PIN_FREQUENCY``           attr current frequency of a pin
+    ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
+    ``PIN_ANY_FREQUENCY_MIN``   attr minimum value of frequency in case
+                                pin/dpll supports any frequency
+    ``PIN_ANY_FREQUENCY_MAX``   attr maximum value of frequency in case
+                                pin/dpll supports any frequency
+    ``PIN_PARENT``              nest attr for each MUX-type parent, that
+                                pin is connected with
+      ``PIN_PARENT_IDX``        attr index of a parent pin on the dpll
+                                device
+      ``PIN_STATE``             attr state of a pin on parent pin
+    ``PIN_RCLK_DEVICE``         attr name of a device, where pin
+                                recovers clock signal from
+    ``PIN_DPLL_CAPS``           attr bitmask of pin-dpll capabilities
+
+  ``PIN_SET``                   command to set pins configuration
+    ``ID``                      attr internal dpll device index
+    ``BUS_NAME``                attr dpll device name (not required if
+                                dpll device ID was provided)
+    ``DEV_NAME``                attr dpll device name (not required if
+                                dpll device ID was provided)
+    ``PIN_IDX``                 attr index of a pin on the dpll device
+    ``PIN_DIRECTION``           attr direction to be set
+    ``PIN_FREQUENCY``           attr frequency to be set
+    ``PIN_PRIO``                attr pin priority to be set
+    ``PIN_STATE``               attr pin state to be set
+    ``PIN_PARENT_IDX``          attr if provided state is to be set with
+                                parent pin instead of with dpll device
+
+Netlink dump requests
+=====================
+
+The ``DEVICE_GET`` and ``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.
+
+
+SET commands format
+===================
+
+``DEVICE_SET`` - to target a dpll device, the user provides either a
+``ID`` or both ``BUS_NAME`` and ``DEV_NAME``, as well as parameter being
+configured (``DPLL_A_MODE``).
+
+``PIN_SET`` - to target a pin user has to provide a ``PIN_IDX``, but
+pin does not exist on its own, thus a dpll device must be also targeted
+with either a ``ID`` or both ``BUS_NAME`` and ``DEV_NAME`` to which
+pin being configured was registered with. Also configured pin parameters
+must be added.
+If ``PIN_DIRECTION`` or ``PIN_FREQUENCY`` are configured, this affects
+all the dpll device they are connected.
+If ``PIN_PRIO`` or ``PIN_STATE`` are configured, this affects only
+the dpll device being targeted.
+If valid ``PIN_PARENT_IDX`` is provided, the set command shall affect
+the configuration between a pin and it's parent, which is a
+``PIN_STATE``.
+In general it is possible to configure multiple parameters at once.
+
+
+Device level configuration pre-defined enums
+=================================================
+
+For all below enum names used for configuration of dpll device use
+the ``DPLL_`` prefix.
+
+Values for ``DPLL_A_LOCK_STATUS`` attribute:
+
+  ============================= ======================================
+  ``LOCK_STATUS_UNLOCKED``      dpll device is in freerun, not locked
+                                to any source pin
+  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
+                                source pin signal
+  ``LOCK_STATUS_LOCKED``        dpll device is locked to the source
+                                pin frequency
+  ``LOCK_STATUS_HOLDOVER``      dpll device lost a lock, using its
+                                frequency holdover capabilities
+
+Values for ``DPLL_A_MODE`` attribute:
+
+  =================== ================================================
+  ``MODE_FORCED``     source pin is force-selected by setting pin
+                      state to ``DPLL_PIN_STATE_CONNECTED`` on a dpll
+  ``MODE_AUTOMATIC``  source pin is auto selected according to
+                      configured pin priorities and source signal
+                      validity
+  ``MODE_HOLDOVER``   force holdover mode of dpll
+  ``MODE_FREERUN``    dpll device is driven by supplied system clock
+                      without holdover capabilities
+  ``MODE_NCO``        similar to FREERUN, with possibility to
+                      numerically control frequency offset
+
+Values for ``DPLL_A_TYPE`` attribute:
+
+  ============= ===================================================
+  ``TYPE_PPS``  dpll device used to provide pulse-per-second output
+  ``TYPE_EEC``  dpll device used to drive ethernet equipment clock
+
+
+
+Pin level configuration pre-defined enums
+=========================================
+
+For all below enum names used for configuration of pin use the
+``DPLL_PIN_`` prefix.
+
+Values for ``DPLL_A_PIN_STATE`` attribute:
+
+  ======================= ========================================
+  ``STATE_CONNECTED``     Pin used as active source for a dpll
+                          device or for a parent pin
+  ``STATE_DISCONNECTED``  Pin disconnected from a dpll device or
+                          from a parent pin
+  ``STATE_SELECTABLE``    Pin enabled for automatic selection
+
+Values for ``DPLL_A_PIN_DIRECTION`` attribute:
+
+  ======================= ==============================
+  ``DIRECTION_SOURCE``    Pin used as a source of signal
+  ``DIRECTION_OUTPUT``    Pin used to output signal
+
+Values for ``DPLL_A_PIN_TYPE`` attributes:
+
+  ======================== ========================================
+  ``TYPE_MUX``             MUX type pin, connected pins shall have
+                           their own types
+  ``TYPE_EXT``             External pin
+  ``TYPE_SYNCE_ETH_PORT``  SyncE on Ethernet port
+  ``TYPE_INT_OSCILLATOR``  Internal Oscillator (i.e. Holdover with
+                           Atomic Clock as a Source)
+  ``TYPE_GNSS``            GNSS 1PPS source
+
+Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
+
+  ============================= ================================
+  ``CAPS_DIRECTION_CAN_CHANGE`` Bit present if direction can change
+  ``CAPS_PRIORITY_CAN_CHANGE``  Bit present if priority can change
+  ``CAPS_STATE_CAN_CHANGE``     Bit present if state can change
+
+
+Notifications
+=============
+
+dpll device can provide notifications regarding status changes of the
+device, i.e. lock status changes, source/output type changes or alarms.
+This is the multicast group that is used to notify user-space apps via
+netlink socket: ``DPLL_MCGRP_MONITOR``
+
+Notifications messages (attrbiutes use ``DPLL_A`` prefix):
+
+  ========================= ==========================================
+  ``EVENT_DEVICE_CREATE``   event value new dpll device was created
+    ``ID``                  attr internal dpll device ID
+    ``DEV_NAME``            attr dpll device name
+    ``BUS_NAME``            attr dpll device bus name
+  ``EVENT_DEVICE_DELETE``   event value dpll device was deleted
+    ``ID``                  attr dpll device index
+  ``EVENT_DEVICE_CHANGE``   event value dpll device attribute has
+                            changed
+    ``ID``                  attr modified dpll device ID
+    ``PIN_IDX``             attr the modified pin index
+
+Device change event shall consiste of the attribute and the value that
+has changed.
+
+
+Device driver implementation
+============================
+
+Device is allocated by ``dpll_device_get`` call. Second call with the
+same arguments doesn't create new object but provides pointer to
+previously created device for given arguments, it also increase 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. Only one driver instance can register a dpll device within dpll
+subsytem. Multiple driver instances can obtain reference to it with
+``dpll_device_get``.
+
+The pins are allocated separately with ``dpll_pin_get``, it works
+similarly to ``dpll_device_get``. Creates object and the for each call
+with the same arguments the object refcount increases.
+
+Once dpll device is created, allocated pin can be registered with it
+with 2 different methods, always providing implemented pin callbacks,
+and private data pointer for calling them:
+``dpll_pin_register`` - simple registration with a dpll device.
+``dpll_pin_on_pin_register`` - register pin with another MUX type pin.
+
+For different instances of a device driver requiring to find already
+registered dpll (i.e. to connect its pins to it) use ``dpll_device_get``
+to obtain proper dpll device pointer.
+
+The name of dpll device is generated based on registerer provided module
+struct pointer, clock_id and device_idx values.
+Name is in format: ``%s/%llx/%d`` where arguments are as follows:
+``module_name(dpll->module)`` - syscall on parent module struct pointer
+``dpll->clock_id``            - registerer given clock id
+``dpll->device_idx``          - registerer given device id
+
+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 shall
+be requested by device driver with ``dpll_device_notify`` or
+``dpll_pin_notify``.
+
+The device driver using dpll interface is not required to implement all
+the callback operation. Nevertheless there are few required to be
+implemented.
+Required dpll device level callback operations:
+- ``.mode_get``
+- ``.lock_status_get``
+
+Required pin level callback operations:
+- ``.state_get``
+- ``.direction_get``
+
+There is no strict requirement to implement all the operations for
+each device, every operation handler is checked for existence and
+ENOTSUPP is returned in case of absence of specific handler.
+
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index a164ff074356..4828d6f6daf1 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -17,6 +17,7 @@ Contents:
    dsa/index
    devlink/index
    caif/index
+   dpll
    ethtool-netlink
    ieee802154
    j1939
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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 <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
 Documentation/dpll.rst             | 408 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 409 insertions(+)
 create mode 100644 Documentation/dpll.rst

diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
new file mode 100644
index 000000000000..fba5bc027967
--- /dev/null
+++ b/Documentation/dpll.rst
@@ -0,0 +1,408 @@
+.. 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
+pins connected with it.
+It provides its supported working modes 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 same command.
+Requesting configuration of dpll device is done with `do` request of
+netlink ``DPLL_CMD_DEVICE_SET`` command.
+
+
+Pin object
+==========
+A pin is amorphic object which represents either source or output, it
+could be internal component of the device, as well as externaly
+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 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 manualy 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
+  source for automatic selection algorithm
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
+  a valid source 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 sources.
+
+
+Shared pins
+===========
+A single pin object can be registered 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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
+2) Set on a pin-dpll tuple - the configuration affects only selected
+   dpll device. (i.e. PIN_PRIO, PIN_STATE).
+
+
+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': [{
+        'device': [{'bus-name': 'pci',
+                    'dev-name': '0000:21:00.0_0', 'id': 0}],
+        'pin-direction': {'doc': 'pin used as a source of a signal',
+                          'name': 'source'},
+        'pin-idx': 13,
+        'pin-parent': [{'pin-parent-idx': 2,
+                        'pin-state': {'doc': 'pin disconnected',
+                                      'name': 'disconnected'}},
+                       {'pin-parent-idx': 3,
+                        'pin-state': {'doc': 'pin disconnected',
+                                      'name': 'disconnected'}}],
+        }]``
+
+Only one child pin can provide it's signal to the parent MUX-type pin at
+a time, the selection is done with requesting change of child pin state
+to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
+index value in ``DPLL_A_PARENT_PIN_IDX``.
+
+Pin priority
+============
+Some devices might offer a capability of automatic pin selection mode
+(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
+Usually such automatic selection is offloaded to the hardware,
+which means only pins directly connected to the dpll are capable of
+automatic source pin selection.
+In automatic selection mode, the user cannot manually select a source
+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 connect with it.
+Child pin of MUX-type is not capable of automatic source pin selection,
+in order to configure a source of a MUX-type pin, the user needs to
+request desired pin state of the child pin on the parent - it is done
+with providing additional attribute for pin set state request - index
+of parent pin he wish to propagate its signal to
+(``DPLL_A_PARENT_PIN_IDX``).
+
+
+Configuration commands group
+============================
+
+Configuration commands are used to get or dump information about
+registered dpll devices (and pins), as well as set configuration of
+device or pins. As dpll device could not be abstract and reflects real
+hardware, there is no way to add new dpll device via netlink from user
+space and each device should be registered by it's driver.
+
+All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
+any spamming/D.o.S. from unauthorized userspace applications.
+
+List of netlink commands with possible attributes
+=================================================
+
+All constants identifying command types use ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use ``DPLL_A_``
+prefix and suffix according to attribute purpose:
+
+  ============================  =======================================
+  ``DEVICE_GET``                command to get device info or dump list
+                                of available devices
+    ``ID``                      attr internal dpll device ID
+    ``DEV_NAME``                attr dpll device name
+    ``BUS_NAME``                attr dpll device bus name
+    ``MODE``                    attr selection mode
+    ``MODE_SUPPORTED``          attr available selection modes
+    ``LOCK_STATUS``             attr internal frequency-lock status
+    ``TEMP``                    attr device temperature information
+    ``CLOCK_ID``                attr Unique Clock Identifier (EUI-64),
+                                as defined by the IEEE 1588 standard
+    ``TYPE``                    attr type or purpose of dpll device
+  ``DEVICE_SET``                command to set dpll device configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``MODE``                    attr selection mode to configure
+  ``PIN_GET``                   command to get pin info or dump list of
+                                available pins
+    ``DEVICE``                  nest attr for each dpll device pin is
+                                connected with
+      ``ID``                    attr internal dpll device ID
+      ``DEV_NAME``              attr dpll device name
+      ``BUS_NAME``              attr dpll device bus name
+      ``PIN_PRIO``              attr priority of pin on the dpll device
+      ``PIN_STATE``             attr state of pin on the dpll device
+    ``PIN_IDX``                 attr index of a pin on the dpll device
+    ``PIN_DESCRIPTION``         attr description provided by driver
+    ``PIN_TYPE``                attr type of a pin
+    ``PIN_DIRECTION``           attr direction of a pin
+    ``PIN_FREQUENCY``           attr current frequency of a pin
+    ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
+    ``PIN_ANY_FREQUENCY_MIN``   attr minimum value of frequency in case
+                                pin/dpll supports any frequency
+    ``PIN_ANY_FREQUENCY_MAX``   attr maximum value of frequency in case
+                                pin/dpll supports any frequency
+    ``PIN_PARENT``              nest attr for each MUX-type parent, that
+                                pin is connected with
+      ``PIN_PARENT_IDX``        attr index of a parent pin on the dpll
+                                device
+      ``PIN_STATE``             attr state of a pin on parent pin
+    ``PIN_RCLK_DEVICE``         attr name of a device, where pin
+                                recovers clock signal from
+    ``PIN_DPLL_CAPS``           attr bitmask of pin-dpll capabilities
+
+  ``PIN_SET``                   command to set pins configuration
+    ``ID``                      attr internal dpll device index
+    ``BUS_NAME``                attr dpll device name (not required if
+                                dpll device ID was provided)
+    ``DEV_NAME``                attr dpll device name (not required if
+                                dpll device ID was provided)
+    ``PIN_IDX``                 attr index of a pin on the dpll device
+    ``PIN_DIRECTION``           attr direction to be set
+    ``PIN_FREQUENCY``           attr frequency to be set
+    ``PIN_PRIO``                attr pin priority to be set
+    ``PIN_STATE``               attr pin state to be set
+    ``PIN_PARENT_IDX``          attr if provided state is to be set with
+                                parent pin instead of with dpll device
+
+Netlink dump requests
+=====================
+
+The ``DEVICE_GET`` and ``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.
+
+
+SET commands format
+===================
+
+``DEVICE_SET`` - to target a dpll device, the user provides either a
+``ID`` or both ``BUS_NAME`` and ``DEV_NAME``, as well as parameter being
+configured (``DPLL_A_MODE``).
+
+``PIN_SET`` - to target a pin user has to provide a ``PIN_IDX``, but
+pin does not exist on its own, thus a dpll device must be also targeted
+with either a ``ID`` or both ``BUS_NAME`` and ``DEV_NAME`` to which
+pin being configured was registered with. Also configured pin parameters
+must be added.
+If ``PIN_DIRECTION`` or ``PIN_FREQUENCY`` are configured, this affects
+all the dpll device they are connected.
+If ``PIN_PRIO`` or ``PIN_STATE`` are configured, this affects only
+the dpll device being targeted.
+If valid ``PIN_PARENT_IDX`` is provided, the set command shall affect
+the configuration between a pin and it's parent, which is a
+``PIN_STATE``.
+In general it is possible to configure multiple parameters at once.
+
+
+Device level configuration pre-defined enums
+=================================================
+
+For all below enum names used for configuration of dpll device use
+the ``DPLL_`` prefix.
+
+Values for ``DPLL_A_LOCK_STATUS`` attribute:
+
+  ============================= ======================================
+  ``LOCK_STATUS_UNLOCKED``      dpll device is in freerun, not locked
+                                to any source pin
+  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
+                                source pin signal
+  ``LOCK_STATUS_LOCKED``        dpll device is locked to the source
+                                pin frequency
+  ``LOCK_STATUS_HOLDOVER``      dpll device lost a lock, using its
+                                frequency holdover capabilities
+
+Values for ``DPLL_A_MODE`` attribute:
+
+  =================== ================================================
+  ``MODE_FORCED``     source pin is force-selected by setting pin
+                      state to ``DPLL_PIN_STATE_CONNECTED`` on a dpll
+  ``MODE_AUTOMATIC``  source pin is auto selected according to
+                      configured pin priorities and source signal
+                      validity
+  ``MODE_HOLDOVER``   force holdover mode of dpll
+  ``MODE_FREERUN``    dpll device is driven by supplied system clock
+                      without holdover capabilities
+  ``MODE_NCO``        similar to FREERUN, with possibility to
+                      numerically control frequency offset
+
+Values for ``DPLL_A_TYPE`` attribute:
+
+  ============= ===================================================
+  ``TYPE_PPS``  dpll device used to provide pulse-per-second output
+  ``TYPE_EEC``  dpll device used to drive ethernet equipment clock
+
+
+
+Pin level configuration pre-defined enums
+=========================================
+
+For all below enum names used for configuration of pin use the
+``DPLL_PIN_`` prefix.
+
+Values for ``DPLL_A_PIN_STATE`` attribute:
+
+  ======================= ========================================
+  ``STATE_CONNECTED``     Pin used as active source for a dpll
+                          device or for a parent pin
+  ``STATE_DISCONNECTED``  Pin disconnected from a dpll device or
+                          from a parent pin
+  ``STATE_SELECTABLE``    Pin enabled for automatic selection
+
+Values for ``DPLL_A_PIN_DIRECTION`` attribute:
+
+  ======================= ==============================
+  ``DIRECTION_SOURCE``    Pin used as a source of signal
+  ``DIRECTION_OUTPUT``    Pin used to output signal
+
+Values for ``DPLL_A_PIN_TYPE`` attributes:
+
+  ======================== ========================================
+  ``TYPE_MUX``             MUX type pin, connected pins shall have
+                           their own types
+  ``TYPE_EXT``             External pin
+  ``TYPE_SYNCE_ETH_PORT``  SyncE on Ethernet port
+  ``TYPE_INT_OSCILLATOR``  Internal Oscillator (i.e. Holdover with
+                           Atomic Clock as a Source)
+  ``TYPE_GNSS``            GNSS 1PPS source
+
+Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
+
+  ============================= ================================
+  ``CAPS_DIRECTION_CAN_CHANGE`` Bit present if direction can change
+  ``CAPS_PRIORITY_CAN_CHANGE``  Bit present if priority can change
+  ``CAPS_STATE_CAN_CHANGE``     Bit present if state can change
+
+
+Notifications
+=============
+
+dpll device can provide notifications regarding status changes of the
+device, i.e. lock status changes, source/output type changes or alarms.
+This is the multicast group that is used to notify user-space apps via
+netlink socket: ``DPLL_MCGRP_MONITOR``
+
+Notifications messages (attrbiutes use ``DPLL_A`` prefix):
+
+  ========================= ==========================================
+  ``EVENT_DEVICE_CREATE``   event value new dpll device was created
+    ``ID``                  attr internal dpll device ID
+    ``DEV_NAME``            attr dpll device name
+    ``BUS_NAME``            attr dpll device bus name
+  ``EVENT_DEVICE_DELETE``   event value dpll device was deleted
+    ``ID``                  attr dpll device index
+  ``EVENT_DEVICE_CHANGE``   event value dpll device attribute has
+                            changed
+    ``ID``                  attr modified dpll device ID
+    ``PIN_IDX``             attr the modified pin index
+
+Device change event shall consiste of the attribute and the value that
+has changed.
+
+
+Device driver implementation
+============================
+
+Device is allocated by ``dpll_device_get`` call. Second call with the
+same arguments doesn't create new object but provides pointer to
+previously created device for given arguments, it also increase 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. Only one driver instance can register a dpll device within dpll
+subsytem. Multiple driver instances can obtain reference to it with
+``dpll_device_get``.
+
+The pins are allocated separately with ``dpll_pin_get``, it works
+similarly to ``dpll_device_get``. Creates object and the for each call
+with the same arguments the object refcount increases.
+
+Once dpll device is created, allocated pin can be registered with it
+with 2 different methods, always providing implemented pin callbacks,
+and private data pointer for calling them:
+``dpll_pin_register`` - simple registration with a dpll device.
+``dpll_pin_on_pin_register`` - register pin with another MUX type pin.
+
+For different instances of a device driver requiring to find already
+registered dpll (i.e. to connect its pins to it) use ``dpll_device_get``
+to obtain proper dpll device pointer.
+
+The name of dpll device is generated based on registerer provided module
+struct pointer, clock_id and device_idx values.
+Name is in format: ``%s/%llx/%d`` where arguments are as follows:
+``module_name(dpll->module)`` - syscall on parent module struct pointer
+``dpll->clock_id``            - registerer given clock id
+``dpll->device_idx``          - registerer given device id
+
+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 shall
+be requested by device driver with ``dpll_device_notify`` or
+``dpll_pin_notify``.
+
+The device driver using dpll interface is not required to implement all
+the callback operation. Nevertheless there are few required to be
+implemented.
+Required dpll device level callback operations:
+- ``.mode_get``
+- ``.lock_status_get``
+
+Required pin level callback operations:
+- ``.state_get``
+- ``.direction_get``
+
+There is no strict requirement to implement all the operations for
+each device, every operation handler is checked for existence and
+ENOTSUPP is returned in case of absence of specific handler.
+
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index a164ff074356..4828d6f6daf1 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -17,6 +17,7 @@ Contents:
    dsa/index
    devlink/index
    caif/index
+   dpll
    ethtool-netlink
    ieee802154
    j1939
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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 <arkadiusz.kubalewski@intel.com>
---
 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   | 411 +++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 240 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1415 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index aa32111afd6e..5736757039db 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -202,6 +202,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 838d9b274d68..e6edc1a90f44 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 450317dfcca7..f590c2a658b4 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4325,13 +4325,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..e9a371fa038b 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,329 @@ 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_sig_type_mask
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: signal type bit mask of a pin.
+ */
+unsigned long
+ice_cgu_get_pin_freq_mask(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].sig_type_mask;
+}
+
+/**
+ * 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..d09e5bca0ff1 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,232 @@ 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
+};
+
+#define MAX_CGU_PIN_NAME_LEN		16
+#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
+					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
+struct ice_cgu_pin_desc {
+	char name[MAX_CGU_PIN_NAME_LEN];
+	u8 index;
+	enum dpll_pin_type type;
+	unsigned long sig_type_mask;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +424,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);
+unsigned long
+ice_cgu_get_pin_freq_mask(struct ice_hw *hw, u8 pin, bool input);
+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.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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 <arkadiusz.kubalewski@intel.com>
---
 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   | 411 +++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 240 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1415 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index aa32111afd6e..5736757039db 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -202,6 +202,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 838d9b274d68..e6edc1a90f44 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 450317dfcca7..f590c2a658b4 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4325,13 +4325,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..e9a371fa038b 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,329 @@ 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_sig_type_mask
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: signal type bit mask of a pin.
+ */
+unsigned long
+ice_cgu_get_pin_freq_mask(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].sig_type_mask;
+}
+
+/**
+ * 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..d09e5bca0ff1 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,232 @@ 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
+};
+
+#define MAX_CGU_PIN_NAME_LEN		16
+#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
+					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
+struct ice_cgu_pin_desc {
+	char name[MAX_CGU_PIN_NAME_LEN];
+	u8 index;
+	enum dpll_pin_type type;
+	unsigned long sig_type_mask;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +424,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);
+unsigned long
+ice_cgu_get_pin_freq_mask(struct ice_hw *hw, u8 pin, bool input);
+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.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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 <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
 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   | 1929 +++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
 drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
 8 files changed, 2125 insertions(+), 89 deletions(-)
 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 5d89392f969b..6c198cd92d49 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -74,6 +74,7 @@
 #include "ice_lag.h"
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -201,6 +202,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,
@@ -512,6 +514,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 */
 };
 
@@ -635,6 +638,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..3217fb36dd12
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,1929 @@
+// 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>
+#include <uapi/linux/dpll.h>
+
+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_LOCK_TRIES		1000
+#define ICE_DPLL_PIN_IDX_INVALID	0xff
+
+/**
+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
+	[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_SOURCE,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
+};
+
+/**
+ * pin_type_name - string names of ice pin types
+ */
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
+};
+
+/**
+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin index of a searched dpll subsystem
+ * pin pointer.
+ *
+ * Return:
+ * * valid index for a given pin & pin type found on pf internal dpll struct
+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
+ */
+static u32
+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
+		 enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return ICE_DPLL_PIN_IDX_INVALID;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else {
+		return ICE_DPLL_PIN_IDX_INVALID;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return i;
+
+	return ICE_DPLL_PIN_IDX_INVALID;
+}
+
+/**
+ * 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_find_pin - find ice_dpll_pin on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin info pointer holding data of given dpll
+ * subsystem pin pointer.
+ *
+ * Return:
+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
+ * found in pf internal pin data.
+ * * NULL - if pin was not found.
+ */
+static struct ice_dpll_pin
+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
+	      enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return NULL;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		if (pin == pf->dplls.rclk.pin)
+			return &pf->dplls.rclk;
+	} else {
+		return NULL;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return &pins[i];
+
+	return NULL;
+}
+
+/**
+ * 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.
+ *
+ * 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,
+		      const enum ice_dpll_pin_type pin_type, const u32 freq)
+{
+	u8 flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		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);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		dev_dbg(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);
+	} else {
+		pin->freq = freq;
+	}
+
+	return ret;
+}
+
+/**
+ * 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
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * 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,
+		       const u32 frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+
+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_frequency_set - source 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_source_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, (u32)frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * 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.
+ *
+ * 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, 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.
+ *
+ * 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, u64 *frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	*frequency = (u64)(p->freq);
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_frequency_get - source 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 source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_source_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, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * 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.
+ *
+ * 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, 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.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags[0];
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	}
+	if (ret)
+		dev_dbg(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);
+	else
+		pin->flags[0] = flags;
+
+	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.
+ *
+ * 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)
+{
+	u8 flags = pin->flags[0];
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	}
+	if (ret)
+		dev_dbg(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);
+	else
+		pin->flags[0] = flags;
+
+	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 source pin states are separated for each
+ * dpll, for rclk pins states are separated for each parent.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			  const enum ice_dpll_pin_type pin_type)
+{
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		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_source ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					pin->pin == pf->dplls.pps.active_source ?
+					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;
+		}
+	} else if (pin_type == 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;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		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;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * ice_find_dpll - find ice_dpll on a pf
+ * @pf: private board structure
+ * @dpll: kernel's dpll_device pointer to be searched
+ *
+ * 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_source_prio_set - set source 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_source_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_dbg(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.
+ *
+ * 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_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		return -EFAULT;
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
+	*status = ice_dpll_status[d->dpll_state];
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	struct ice_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	ice_dpll_cb_unlock(pf);
+	if (!d)
+		return -EFAULT;
+	*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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return false;
+
+	if (ice_dpll_cb_lock(pf))
+		return false;
+	d = ice_find_dpll(pf, dpll);
+	ice_dpll_cb_unlock(pf);
+	if (!d)
+		return false;
+	if (mode == DPLL_MODE_AUTOMATIC)
+		return true;
+
+	return false;
+}
+
+/**
+ * ice_dpll_pin_state_set - set pin's state on dpll
+ * @dpll: dpll being configured
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ * @pin_type: type of a pin
+ *
+ * Set pin state on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_state_set(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin, void *pin_priv,
+		       const enum dpll_pin_state state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		if (state == DPLL_PIN_STATE_SELECTABLE)
+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		if (state == DPLL_PIN_STATE_CONNECTED)
+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	}
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
+unlock:
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
+		__func__, dpll, pin, p, pf, state, 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.
+ *
+ * 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,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_source_state_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)
+{
+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_pin_state_get - set pin's state on dpll
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_pin_state_get(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin, void *pin_priv,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	struct ice_dpll *d;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	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_SOURCE)
+		*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);
+	dev_dbg(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.
+ *
+ * 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(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_get - get source 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 source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_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_pf *pf = pin_priv;
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		NL_SET_ERR_MSG(extack, "dpll not found");
+		goto unlock;
+	}
+	*prio = d->input_prio[p->idx];
+	ret = 0;
+unlock:
+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_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_pf *pf = pin_priv;
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (prio > ICE_DPLL_PRIO_MAX) {
+		NL_SET_ERR_MSG(extack, "prio out of range");
+		return ret;
+	}
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		NL_SET_ERR_MSG(extack, "dpll not found");
+		goto unlock;
+	}
+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);
+unlock:
+	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_source_direction - callback for get source 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 source pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int ice_dpll_source_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_SOURCE;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_source_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
+ *
+ * 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,
+					  const enum dpll_pin_state state,
+					  struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	parent_idx = ice_find_pin_idx(pf, parent_pin,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
+			hw_idx = i;
+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+
+	if ((enable && !!(p->flags[hw_idx] &
+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
+	    (!enable && !(p->flags[hw_idx] &
+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
+		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 ret:%d\n",
+		__func__, parent_pin, pin, pf, 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.
+ *
+ * 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,
+					  enum dpll_pin_state *state,
+					  struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = pin_priv;
+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p)
+		goto unlock;
+	parent_idx = ice_find_pin_idx(pf, parent_pin,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
+			hw_idx = i;
+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+
+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (ret)
+		goto unlock;
+
+	if (!!(p->flags[hw_idx] &
+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))
+		*state = DPLL_PIN_STATE_CONNECTED;
+	else
+		*state = DPLL_PIN_STATE_DISCONNECTED;
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, parent_pin, pin, pf, ret);
+
+	return ret;
+}
+
+static 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_source_direction,
+};
+
+static struct dpll_pin_ops ice_dpll_source_ops = {
+	.frequency_get = ice_dpll_source_frequency_get,
+	.frequency_set = ice_dpll_source_frequency_set,
+	.state_on_dpll_get = ice_dpll_source_state_get,
+	.state_on_dpll_set = ice_dpll_source_state_set,
+	.prio_get = ice_dpll_source_prio_get,
+	.prio_set = ice_dpll_source_prio_set,
+	.direction_get = ice_dpll_source_direction,
+};
+
+static 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 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_release_info - release memory allocated for pins
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ */
+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
+ * @pf: board private structure
+ *
+ * Deregister from parent pins and release resources in dpll subsystem.
+ */
+static void
+ice_dpll_release_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
+	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;
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, pf);
+	}
+	dpll_pin_put(rclk->pin);
+	rclk->pin = NULL;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @pf: board private structure
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Deregister and free pins of a given array of pins from dpll devices
+ * registered in dpll subsystem.
+ */
+static void
+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count, struct dpll_pin_ops *ops, bool cgu)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		struct ice_dpll_pin *p = &pins[i];
+
+		if (p && !IS_ERR_OR_NULL(p->pin)) {
+			if (cgu && dpll_eec)
+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
+			if (cgu && dpll_pps)
+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
+			dpll_pin_put(p->pin);
+			p->pin = NULL;
+		}
+	}
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	u32 rclk_idx;
+	int ret, i;
+
+	ops = &ice_dpll_source_ops;
+	pins = pf->dplls.inputs;
+	for (i = 0; i < pf->dplls.num_inputs; i++) {
+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
+					   THIS_MODULE, &pins[i].prop);
+		if (IS_ERR_OR_NULL(pins[i].pin)) {
+			pins[i].pin = NULL;
+			return -ENOMEM;
+		}
+		if (cgu) {
+			ret = dpll_pin_register(pf->dplls.eec.dpll,
+						pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+			ret = dpll_pin_register(pf->dplls.pps.dpll,
+						pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+		}
+	}
+	if (cgu) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		for (i = 0; i < pf->dplls.num_outputs; i++) {
+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
+						   i + pf->dplls.num_inputs,
+						   THIS_MODULE, &pins[i].prop);
+			if (IS_ERR_OR_NULL(pins[i].pin)) {
+				pins[i].pin = NULL;
+				return -ENOMEM;
+			}
+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+		}
+	}
+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
+					  THIS_MODULE, &pf->dplls.rclk.prop);
+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
+		pf->dplls.rclk.pin = NULL;
+		return -ENOMEM;
+	}
+	ops = &ice_dpll_rclk_ops;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+		struct dpll_pin *parent =
+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
+
+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
+					       ops, pf, dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ * @clock_id: holds generated clock_id
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ */
+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
+{
+	*clock_id = pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_init_dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Get dplls instances for this board, if cgu is controlled by this NIC,
+ * register dpll with callbacks ops
+ *
+ * Return:
+ * * 0 - success
+ * * negative - allocation fails
+ */
+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	int ret = -ENOMEM;
+	u64 clock_id;
+
+	ice_generate_clock_id(pf, &clock_id);
+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf->dplls.eec.dpll_idx,
+					     THIS_MODULE);
+	if (!pf->dplls.eec.dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
+		return ret;
+	}
+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf->dplls.pps.dpll_idx,
+					     THIS_MODULE);
+	if (!pf->dplls.pps.dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
+		goto put_eec;
+	}
+
+	if (cgu) {
+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
+					   &ice_dpll_ops, pf, dev);
+		if (ret)
+			goto put_pps;
+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
+					   &ice_dpll_ops, pf, dev);
+		if (ret)
+			goto put_pps;
+	}
+
+	return 0;
+
+put_pps:
+	dpll_device_put(pf->dplls.pps.dpll);
+	pf->dplls.pps.dpll = NULL;
+put_eec:
+	dpll_device_put(pf->dplls.eec.dpll);
+	pf->dplls.eec.dpll = NULL;
+
+	return ret;
+}
+
+/**
+ * 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.
+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
+		p = &pf->dplls.inputs[d->source_idx];
+		return ice_dpll_pin_state_update(pf, p,
+						 ICE_DPLL_PIN_TYPE_SOURCE);
+	}
+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
+		d->active_source = NULL;
+		p = &pf->dplls.inputs[d->source_idx];
+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
+		ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+	} else if (d->source_idx != d->prev_source_idx) {
+		p = &pf->dplls.inputs[d->prev_source_idx];
+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+		p = &pf->dplls.inputs[d->source_idx];
+		d->active_source = p->pin;
+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
+	}
+	if (d->prev_source != d->active_source) {
+		d->prev_source = d->active_source;
+		if (d->active_source)
+			dpll_pin_notify(d->dpll, d->active_source,
+					DPLL_A_PIN_STATE);
+	}
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ */
+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.
+ *
+ * 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_release_all - disable support for DPLL and unregister dpll device
+ * @pf: board private structure
+ * @cgu: if cgu is controlled by this driver instance
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+
+	mutex_lock(&pf->dplls.lock);
+	ice_dpll_release_rclk_pin(pf);
+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
+			      d->num_inputs, &ice_dpll_source_ops, cgu);
+	mutex_unlock(&pf->dplls.lock);
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
+				      d->num_outputs,
+				      &ice_dpll_output_ops, cgu);
+		mutex_unlock(&pf->dplls.lock);
+	}
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
+		dpll_device_put(dp->dpll);
+		mutex_unlock(&pf->dplls.lock);
+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
+	}
+
+	if (de->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
+		dpll_device_put(de->dpll);
+		mutex_unlock(&pf->dplls.lock);
+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
+	}
+
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		kthread_cancel_delayed_work_sync(&d->work);
+		if (d->kworker) {
+			kthread_destroy_worker(d->kworker);
+			d->kworker = NULL;
+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
+		}
+		mutex_unlock(&pf->dplls.lock);
+	}
+}
+
+/**
+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * Handles the cleanup work required after dpll initialization,
+ * freeing resources and unregistering the dpll.
+ */
+void ice_dpll_release(struct ice_pf *pf)
+{
+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		ice_dpll_release_all(pf,
+				     ice_is_feature_supported(pf, ICE_F_CGU));
+		mutex_destroy(&pf->dplls.lock);
+		clear_bit(ICE_FLAG_DPLL, pf->flags);
+	}
+}
+
+/**
+ * ice_dpll_init_direct_pins - initializes source or output pins information
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information about input or output pins, cache them in pins struct.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int
+ice_dpll_init_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;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+		input = true;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+		input = false;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].prop.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;
+	}
+
+	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
+ */
+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.label = dev_name(dev);
+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+
+	return ice_dpll_pin_state_update(pf, pin,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_init_pins - init pins wrapper
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Wraps functions for pin inti.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
+		return ice_dpll_init_direct_pins(pf, pin_type);
+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
+		return ice_dpll_init_direct_pins(pf, pin_type);
+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
+		return ice_dpll_init_rclk_pin(pf);
+	else
+		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 - error
+ */
+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;
+	u8 base_rclk_idx;
+
+	ice_generate_clock_id(pf, &d->clock_id);
+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (ret)
+		goto release_info;
+
+	if (cgu) {
+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+		if (!d->outputs)
+			goto release_info;
+
+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+		if (ret)
+			goto release_info;
+	}
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	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;
+
+release_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_release_info(pf);
+	return ret;
+}
+
+/**
+ * ice_dpll_init - initialize dplls support
+ * @pf: board private structure
+ *
+ * Set up the device dplls registering 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
+ */
+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 release;
+	err = ice_dpll_init_dplls(pf, cgu_present);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, cgu_present);
+	if (err)
+		goto release;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	if (cgu_present) {
+		err = ice_dpll_init_worker(pf);
+		if (err)
+			goto release;
+	}
+	mutex_unlock(&d->lock);
+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
+
+	return err;
+
+release:
+	ice_dpll_release_all(pf, cgu_present);
+	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\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..aad48b9910b7
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,101 @@
+/* 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
+ * @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;
+	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
+ * @dpll_idx: index of dpll on the NIC
+ * @source_idx: source currently selected
+ * @prev_source_idx: source previously selected
+ * @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_source: pointer to active source pin
+ * @prev_source: pointer to previous active source pin
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	int dpll_idx;
+	u8 source_idx;
+	u8 prev_source_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_source;
+	struct dpll_pin *prev_source;
+};
+
+/** 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
+ * @num_rclk: number of recovered clock pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ */
+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_release(struct ice_pf *pf);
+
+int ice_dpll_rclk_init(struct ice_pf *pf);
+
+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4821,6 +4821,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");
@@ -4847,6 +4851,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_release(pf);
 }
 
 static void ice_init_wakeup(struct ice_pf *pf)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index e9a371fa038b..39b692945f73 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
 }
 
 /**
- * ice_cgu_get_pin_sig_type_mask
+ * 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
  *
- * Return: signal type bit mask of a pin.
+ * Get frequency supported number and array of supported frequencies.
+ *
+ * Return: array of supported frequencies for given pin.
  */
-unsigned long
-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
 	int t_size;
 
+	*num = 0;
 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
-
 	if (!t)
-		return 0;
-
+		return NULL;
 	if (pin >= t_size)
-		return 0;
+		return NULL;
+	*num = t[pin].freq_supp_num;
 
-	return t[pin].sig_type_mask;
+	return t[pin].freq_supp;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index d09e5bca0ff1..4568b0403cd7 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
 	NUM_SI_CGU_OUTPUT_PINS
 };
 
-#define MAX_CGU_PIN_NAME_LEN		16
-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
+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[MAX_CGU_PIN_NAME_LEN];
+	char *name;
 	u8 index;
 	enum dpll_pin_type type;
-	unsigned long sig_type_mask;
+	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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
 };
 
@@ -429,8 +419,8 @@ 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);
-unsigned long
-ice_cgu_get_pin_freq_mask(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,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

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 <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
 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   | 1929 +++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
 drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
 8 files changed, 2125 insertions(+), 89 deletions(-)
 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 5d89392f969b..6c198cd92d49 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -74,6 +74,7 @@
 #include "ice_lag.h"
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -201,6 +202,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,
@@ -512,6 +514,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 */
 };
 
@@ -635,6 +638,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..3217fb36dd12
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,1929 @@
+// 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>
+#include <uapi/linux/dpll.h>
+
+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_LOCK_TRIES		1000
+#define ICE_DPLL_PIN_IDX_INVALID	0xff
+
+/**
+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
+	[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_SOURCE,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
+};
+
+/**
+ * pin_type_name - string names of ice pin types
+ */
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
+};
+
+/**
+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin index of a searched dpll subsystem
+ * pin pointer.
+ *
+ * Return:
+ * * valid index for a given pin & pin type found on pf internal dpll struct
+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
+ */
+static u32
+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
+		 enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return ICE_DPLL_PIN_IDX_INVALID;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else {
+		return ICE_DPLL_PIN_IDX_INVALID;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return i;
+
+	return ICE_DPLL_PIN_IDX_INVALID;
+}
+
+/**
+ * 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_find_pin - find ice_dpll_pin on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin info pointer holding data of given dpll
+ * subsystem pin pointer.
+ *
+ * Return:
+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
+ * found in pf internal pin data.
+ * * NULL - if pin was not found.
+ */
+static struct ice_dpll_pin
+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
+	      enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return NULL;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		if (pin == pf->dplls.rclk.pin)
+			return &pf->dplls.rclk;
+	} else {
+		return NULL;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return &pins[i];
+
+	return NULL;
+}
+
+/**
+ * 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.
+ *
+ * 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,
+		      const enum ice_dpll_pin_type pin_type, const u32 freq)
+{
+	u8 flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		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);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		dev_dbg(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);
+	} else {
+		pin->freq = freq;
+	}
+
+	return ret;
+}
+
+/**
+ * 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
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * 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,
+		       const u32 frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+
+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_frequency_set - source 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_source_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, (u32)frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * 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.
+ *
+ * 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, 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.
+ *
+ * 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, u64 *frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	*frequency = (u64)(p->freq);
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_frequency_get - source 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 source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_source_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, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * 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.
+ *
+ * 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, 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.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags[0];
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	}
+	if (ret)
+		dev_dbg(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);
+	else
+		pin->flags[0] = flags;
+
+	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.
+ *
+ * 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)
+{
+	u8 flags = pin->flags[0];
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	}
+	if (ret)
+		dev_dbg(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);
+	else
+		pin->flags[0] = flags;
+
+	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 source pin states are separated for each
+ * dpll, for rclk pins states are separated for each parent.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			  const enum ice_dpll_pin_type pin_type)
+{
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		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_source ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					pin->pin == pf->dplls.pps.active_source ?
+					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;
+		}
+	} else if (pin_type == 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;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		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;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * ice_find_dpll - find ice_dpll on a pf
+ * @pf: private board structure
+ * @dpll: kernel's dpll_device pointer to be searched
+ *
+ * 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_source_prio_set - set source 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_source_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_dbg(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.
+ *
+ * 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_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		return -EFAULT;
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
+	*status = ice_dpll_status[d->dpll_state];
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * 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)
+{
+	struct ice_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	ice_dpll_cb_unlock(pf);
+	if (!d)
+		return -EFAULT;
+	*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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = priv;
+	struct ice_dpll *d;
+
+	if (!pf)
+		return false;
+
+	if (ice_dpll_cb_lock(pf))
+		return false;
+	d = ice_find_dpll(pf, dpll);
+	ice_dpll_cb_unlock(pf);
+	if (!d)
+		return false;
+	if (mode == DPLL_MODE_AUTOMATIC)
+		return true;
+
+	return false;
+}
+
+/**
+ * ice_dpll_pin_state_set - set pin's state on dpll
+ * @dpll: dpll being configured
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ * @pin_type: type of a pin
+ *
+ * Set pin state on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_state_set(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin, void *pin_priv,
+		       const enum dpll_pin_state state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		if (state == DPLL_PIN_STATE_SELECTABLE)
+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		if (state == DPLL_PIN_STATE_CONNECTED)
+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	}
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
+unlock:
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
+		__func__, dpll, pin, p, pf, state, 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.
+ *
+ * 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,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_source_state_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)
+{
+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_pin_state_get - set pin's state on dpll
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin 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.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_pin_state_get(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin, void *pin_priv,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	struct ice_dpll *d;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	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_SOURCE)
+		*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);
+	dev_dbg(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.
+ *
+ * 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(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_get - get source 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 source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_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_pf *pf = pin_priv;
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		NL_SET_ERR_MSG(extack, "dpll not found");
+		goto unlock;
+	}
+	*prio = d->input_prio[p->idx];
+	ret = 0;
+unlock:
+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_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_pf *pf = pin_priv;
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+
+	if (prio > ICE_DPLL_PRIO_MAX) {
+		NL_SET_ERR_MSG(extack, "prio out of range");
+		return ret;
+	}
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p) {
+		NL_SET_ERR_MSG(extack, "pin not found");
+		goto unlock;
+	}
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		NL_SET_ERR_MSG(extack, "dpll not found");
+		goto unlock;
+	}
+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);
+unlock:
+	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_source_direction - callback for get source 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 source pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int ice_dpll_source_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_SOURCE;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_source_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
+ *
+ * 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,
+					  const enum dpll_pin_state state,
+					  struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
+	struct ice_pf *pf = pin_priv;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	parent_idx = ice_find_pin_idx(pf, parent_pin,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
+			hw_idx = i;
+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+
+	if ((enable && !!(p->flags[hw_idx] &
+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
+	    (!enable && !(p->flags[hw_idx] &
+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
+		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 ret:%d\n",
+		__func__, parent_pin, pin, pf, 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.
+ *
+ * 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,
+					  enum dpll_pin_state *state,
+					  struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = pin_priv;
+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	if (!pf)
+		return ret;
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p)
+		goto unlock;
+	parent_idx = ice_find_pin_idx(pf, parent_pin,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
+			hw_idx = i;
+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
+		goto unlock;
+
+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (ret)
+		goto unlock;
+
+	if (!!(p->flags[hw_idx] &
+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))
+		*state = DPLL_PIN_STATE_CONNECTED;
+	else
+		*state = DPLL_PIN_STATE_DISCONNECTED;
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, parent_pin, pin, pf, ret);
+
+	return ret;
+}
+
+static 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_source_direction,
+};
+
+static struct dpll_pin_ops ice_dpll_source_ops = {
+	.frequency_get = ice_dpll_source_frequency_get,
+	.frequency_set = ice_dpll_source_frequency_set,
+	.state_on_dpll_get = ice_dpll_source_state_get,
+	.state_on_dpll_set = ice_dpll_source_state_set,
+	.prio_get = ice_dpll_source_prio_get,
+	.prio_set = ice_dpll_source_prio_set,
+	.direction_get = ice_dpll_source_direction,
+};
+
+static 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 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_release_info - release memory allocated for pins
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ */
+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
+ * @pf: board private structure
+ *
+ * Deregister from parent pins and release resources in dpll subsystem.
+ */
+static void
+ice_dpll_release_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
+	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;
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, pf);
+	}
+	dpll_pin_put(rclk->pin);
+	rclk->pin = NULL;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @pf: board private structure
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Deregister and free pins of a given array of pins from dpll devices
+ * registered in dpll subsystem.
+ */
+static void
+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count, struct dpll_pin_ops *ops, bool cgu)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		struct ice_dpll_pin *p = &pins[i];
+
+		if (p && !IS_ERR_OR_NULL(p->pin)) {
+			if (cgu && dpll_eec)
+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
+			if (cgu && dpll_pps)
+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
+			dpll_pin_put(p->pin);
+			p->pin = NULL;
+		}
+	}
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	u32 rclk_idx;
+	int ret, i;
+
+	ops = &ice_dpll_source_ops;
+	pins = pf->dplls.inputs;
+	for (i = 0; i < pf->dplls.num_inputs; i++) {
+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
+					   THIS_MODULE, &pins[i].prop);
+		if (IS_ERR_OR_NULL(pins[i].pin)) {
+			pins[i].pin = NULL;
+			return -ENOMEM;
+		}
+		if (cgu) {
+			ret = dpll_pin_register(pf->dplls.eec.dpll,
+						pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+			ret = dpll_pin_register(pf->dplls.pps.dpll,
+						pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+		}
+	}
+	if (cgu) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		for (i = 0; i < pf->dplls.num_outputs; i++) {
+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
+						   i + pf->dplls.num_inputs,
+						   THIS_MODULE, &pins[i].prop);
+			if (IS_ERR_OR_NULL(pins[i].pin)) {
+				pins[i].pin = NULL;
+				return -ENOMEM;
+			}
+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
+						ops, pf, NULL);
+			if (ret)
+				return ret;
+		}
+	}
+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
+					  THIS_MODULE, &pf->dplls.rclk.prop);
+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
+		pf->dplls.rclk.pin = NULL;
+		return -ENOMEM;
+	}
+	ops = &ice_dpll_rclk_ops;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+		struct dpll_pin *parent =
+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
+
+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
+					       ops, pf, dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ * @clock_id: holds generated clock_id
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ */
+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
+{
+	*clock_id = pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_init_dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Get dplls instances for this board, if cgu is controlled by this NIC,
+ * register dpll with callbacks ops
+ *
+ * Return:
+ * * 0 - success
+ * * negative - allocation fails
+ */
+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	int ret = -ENOMEM;
+	u64 clock_id;
+
+	ice_generate_clock_id(pf, &clock_id);
+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf->dplls.eec.dpll_idx,
+					     THIS_MODULE);
+	if (!pf->dplls.eec.dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
+		return ret;
+	}
+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf->dplls.pps.dpll_idx,
+					     THIS_MODULE);
+	if (!pf->dplls.pps.dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
+		goto put_eec;
+	}
+
+	if (cgu) {
+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
+					   &ice_dpll_ops, pf, dev);
+		if (ret)
+			goto put_pps;
+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
+					   &ice_dpll_ops, pf, dev);
+		if (ret)
+			goto put_pps;
+	}
+
+	return 0;
+
+put_pps:
+	dpll_device_put(pf->dplls.pps.dpll);
+	pf->dplls.pps.dpll = NULL;
+put_eec:
+	dpll_device_put(pf->dplls.eec.dpll);
+	pf->dplls.eec.dpll = NULL;
+
+	return ret;
+}
+
+/**
+ * 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.
+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
+		p = &pf->dplls.inputs[d->source_idx];
+		return ice_dpll_pin_state_update(pf, p,
+						 ICE_DPLL_PIN_TYPE_SOURCE);
+	}
+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
+		d->active_source = NULL;
+		p = &pf->dplls.inputs[d->source_idx];
+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
+		ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+	} else if (d->source_idx != d->prev_source_idx) {
+		p = &pf->dplls.inputs[d->prev_source_idx];
+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+		p = &pf->dplls.inputs[d->source_idx];
+		d->active_source = p->pin;
+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
+	}
+	if (d->prev_source != d->active_source) {
+		d->prev_source = d->active_source;
+		if (d->active_source)
+			dpll_pin_notify(d->dpll, d->active_source,
+					DPLL_A_PIN_STATE);
+	}
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ */
+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.
+ *
+ * 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_release_all - disable support for DPLL and unregister dpll device
+ * @pf: board private structure
+ * @cgu: if cgu is controlled by this driver instance
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+
+	mutex_lock(&pf->dplls.lock);
+	ice_dpll_release_rclk_pin(pf);
+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
+			      d->num_inputs, &ice_dpll_source_ops, cgu);
+	mutex_unlock(&pf->dplls.lock);
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
+				      d->num_outputs,
+				      &ice_dpll_output_ops, cgu);
+		mutex_unlock(&pf->dplls.lock);
+	}
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
+		dpll_device_put(dp->dpll);
+		mutex_unlock(&pf->dplls.lock);
+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
+	}
+
+	if (de->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
+		dpll_device_put(de->dpll);
+		mutex_unlock(&pf->dplls.lock);
+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
+	}
+
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		kthread_cancel_delayed_work_sync(&d->work);
+		if (d->kworker) {
+			kthread_destroy_worker(d->kworker);
+			d->kworker = NULL;
+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
+		}
+		mutex_unlock(&pf->dplls.lock);
+	}
+}
+
+/**
+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * Handles the cleanup work required after dpll initialization,
+ * freeing resources and unregistering the dpll.
+ */
+void ice_dpll_release(struct ice_pf *pf)
+{
+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		ice_dpll_release_all(pf,
+				     ice_is_feature_supported(pf, ICE_F_CGU));
+		mutex_destroy(&pf->dplls.lock);
+		clear_bit(ICE_FLAG_DPLL, pf->flags);
+	}
+}
+
+/**
+ * ice_dpll_init_direct_pins - initializes source or output pins information
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information about input or output pins, cache them in pins struct.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int
+ice_dpll_init_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;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+		input = true;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+		input = false;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].prop.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;
+	}
+
+	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
+ */
+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.label = dev_name(dev);
+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+
+	return ice_dpll_pin_state_update(pf, pin,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_init_pins - init pins wrapper
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Wraps functions for pin inti.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
+		return ice_dpll_init_direct_pins(pf, pin_type);
+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
+		return ice_dpll_init_direct_pins(pf, pin_type);
+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
+		return ice_dpll_init_rclk_pin(pf);
+	else
+		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 - error
+ */
+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;
+	u8 base_rclk_idx;
+
+	ice_generate_clock_id(pf, &d->clock_id);
+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (ret)
+		goto release_info;
+
+	if (cgu) {
+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+		if (!d->outputs)
+			goto release_info;
+
+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+		if (ret)
+			goto release_info;
+	}
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	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;
+
+release_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_release_info(pf);
+	return ret;
+}
+
+/**
+ * ice_dpll_init - initialize dplls support
+ * @pf: board private structure
+ *
+ * Set up the device dplls registering 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
+ */
+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 release;
+	err = ice_dpll_init_dplls(pf, cgu_present);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, cgu_present);
+	if (err)
+		goto release;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	if (cgu_present) {
+		err = ice_dpll_init_worker(pf);
+		if (err)
+			goto release;
+	}
+	mutex_unlock(&d->lock);
+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
+
+	return err;
+
+release:
+	ice_dpll_release_all(pf, cgu_present);
+	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\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..aad48b9910b7
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,101 @@
+/* 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
+ * @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;
+	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
+ * @dpll_idx: index of dpll on the NIC
+ * @source_idx: source currently selected
+ * @prev_source_idx: source previously selected
+ * @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_source: pointer to active source pin
+ * @prev_source: pointer to previous active source pin
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	int dpll_idx;
+	u8 source_idx;
+	u8 prev_source_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_source;
+	struct dpll_pin *prev_source;
+};
+
+/** 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
+ * @num_rclk: number of recovered clock pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ */
+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_release(struct ice_pf *pf);
+
+int ice_dpll_rclk_init(struct ice_pf *pf);
+
+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4821,6 +4821,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");
@@ -4847,6 +4851,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_release(pf);
 }
 
 static void ice_init_wakeup(struct ice_pf *pf)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index e9a371fa038b..39b692945f73 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
 }
 
 /**
- * ice_cgu_get_pin_sig_type_mask
+ * 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
  *
- * Return: signal type bit mask of a pin.
+ * Get frequency supported number and array of supported frequencies.
+ *
+ * Return: array of supported frequencies for given pin.
  */
-unsigned long
-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
 	int t_size;
 
+	*num = 0;
 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
-
 	if (!t)
-		return 0;
-
+		return NULL;
 	if (pin >= t_size)
-		return 0;
+		return NULL;
+	*num = t[pin].freq_supp_num;
 
-	return t[pin].sig_type_mask;
+	return t[pin].freq_supp;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index d09e5bca0ff1..4568b0403cd7 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
 	NUM_SI_CGU_OUTPUT_PINS
 };
 
-#define MAX_CGU_PIN_NAME_LEN		16
-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
+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[MAX_CGU_PIN_NAME_LEN];
+	char *name;
 	u8 index;
 	enum dpll_pin_type type;
-	unsigned long sig_type_mask;
+	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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+	{ "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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
+		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,
-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
 };
 
@@ -429,8 +419,8 @@ 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);
-unsigned long
-ice_cgu_get_pin_freq_mask(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,
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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

Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
@@ -261,12 +262,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 {
@@ -295,6 +305,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;
@@ -351,8 +362,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)
@@ -836,6 +848,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[] = {
@@ -856,31 +869,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 },
@@ -891,15 +904,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 },
 	{ }
 };
 
@@ -2283,22 +2296,34 @@ 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 = {
+		.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.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;
 	}
@@ -2308,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);
@@ -2330,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;
@@ -2397,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);
 }
@@ -2446,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 = {
+		.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;
 
@@ -2460,16 +2493,16 @@ 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.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) {
@@ -2480,9 +2513,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;
 		}
 	}
@@ -2549,6 +2586,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);
@@ -2690,16 +2730,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;
@@ -2734,6 +2767,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)
@@ -4172,12 +4219,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_SOURCE :
+				  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_SOURCE ?
+			    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) {
@@ -4227,8 +4410,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, &pdev->dev);
+	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], NULL);
+		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:
@@ -4243,7 +4457,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.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

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

Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
@@ -261,12 +262,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 {
@@ -295,6 +305,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;
@@ -351,8 +362,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)
@@ -836,6 +848,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[] = {
@@ -856,31 +869,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 },
@@ -891,15 +904,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 },
 	{ }
 };
 
@@ -2283,22 +2296,34 @@ 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 = {
+		.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.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;
 	}
@@ -2308,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);
@@ -2330,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;
@@ -2397,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);
 }
@@ -2446,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 = {
+		.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;
 
@@ -2460,16 +2493,16 @@ 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.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) {
@@ -2480,9 +2513,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;
 		}
 	}
@@ -2549,6 +2586,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);
@@ -2690,16 +2730,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;
@@ -2734,6 +2767,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)
@@ -4172,12 +4219,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_SOURCE :
+				  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_SOURCE ?
+			    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) {
@@ -4227,8 +4410,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, &pdev->dev);
+	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], NULL);
+		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:
@@ -4243,7 +4457,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.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

From: Jiri Pirko <jiri@nvidia.com>

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 handle fields do not
change. Therefore it is save to access them lockless. It is drivers
responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_netlink.c  | 23 ++++++++++++++++++++--
 include/linux/dpll.h         | 20 +++++++++++++++++++
 include/linux/netdevice.h    |  7 +++++++
 include/uapi/linux/if_link.h |  2 ++
 net/core/dev.c               | 20 +++++++++++++++++++
 net/core/rtnetlink.c         | 38 ++++++++++++++++++++++++++++++++++++
 6 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 1eb0b4a2fce4..734b6776b07c 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -269,8 +269,9 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
 {
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
@@ -290,6 +291,24 @@ 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)
+{
+	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
+	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
+	return nla_total_size(4); /* DPLL_A_PIN_IDX */
+}
+EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
+	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		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)
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 5194efaf55a8..5945bb456794 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -104,6 +104,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
+
 /**
  * dpll_device_get - find or create dpll_device object
  * @clock_id: a system unique number for a device
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 08fbd4622ccf..be162d8db611 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
@@ -2411,6 +2412,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 +3959,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 4ac1000b0ef2..9244d6ff23fc 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 735096d42c1d..fe594e23c1cb 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8990,6 +8990,26 @@ 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)
+{
+	rtnl_lock();
+	dev->dpll_pin = dpll_pin;
+	rtnl_unlock();
+}
+
+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 653901a1bf75..2d1f5d2024ac 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.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

From: Jiri Pirko <jiri@nvidia.com>

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 handle fields do not
change. Therefore it is save to access them lockless. It is drivers
responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_netlink.c  | 23 ++++++++++++++++++++--
 include/linux/dpll.h         | 20 +++++++++++++++++++
 include/linux/netdevice.h    |  7 +++++++
 include/uapi/linux/if_link.h |  2 ++
 net/core/dev.c               | 20 +++++++++++++++++++
 net/core/rtnetlink.c         | 38 ++++++++++++++++++++++++++++++++++++
 6 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 1eb0b4a2fce4..734b6776b07c 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -269,8 +269,9 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
 {
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
@@ -290,6 +291,24 @@ 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)
+{
+	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
+	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
+	return nla_total_size(4); /* DPLL_A_PIN_IDX */
+}
+EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
+	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
+		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)
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 5194efaf55a8..5945bb456794 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -104,6 +104,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
+
 /**
  * dpll_device_get - find or create dpll_device object
  * @clock_id: a system unique number for a device
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 08fbd4622ccf..be162d8db611 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
@@ -2411,6 +2412,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 +3959,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 4ac1000b0ef2..9244d6ff23fc 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 735096d42c1d..fe594e23c1cb 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8990,6 +8990,26 @@ 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)
+{
+	rtnl_lock();
+	dev->dpll_pin = dpll_pin;
+	rtnl_unlock();
+}
+
+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 653901a1bf75..2d1f5d2024ac 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.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 8/8] mlx5: Implement SyncE support using DPLL infrastructure
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-04-28  0:20   ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

From: Jiri Pirko <jiri@nvidia.com>

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 <jiri@nvidia.com>
---
 .../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    | 438 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 526 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..1469ddbafec7
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,438 @@
+// 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>
+
+// POSSIBLE DPLL API EXTENSIONS:
+// 1) Expose clock quality: MSECQ->local_ssm_code, MSECQ->local_enhanced_ssm_code
+//    Proposed enum:
+//      QL_DNU,
+//      QL_EEC1,
+//      QL_eEEC,
+//      QL_SSU_B,
+//      QL_SSU_A,
+//      QL_PRC,
+//      QL_ePRC,
+//      QL_PRTC,
+//      QL_ePRTC,
+// 2) Expose possibility to do holdover: MSEES->ho_acq
+// 3) DPLL Implementation hw-speficic values (debug?): MSEES->oper_freq_measure
+
+/* 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)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+	int err;
+
+	MLX5_SET(msees_reg, in, local_port, 1);
+	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);
+	if (oper_status)
+		*oper_status = MLX5_GET(msees_reg, out, oper_status);
+	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, local_port, 1);
+	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);
+	MLX5_SET(msees_reg, in, admin_freq_measure,
+		 admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK);
+	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				    MLX5_REG_MSEES, 0, 0);
+}
+
+static enum dpll_lock_status
+mlx5_dpll_lock_status_from_oper_status(enum mlx5_msees_oper_status oper_status)
+{
+	switch (oper_status) {
+	case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
+		return DPLL_LOCK_STATUS_LOCKED;
+	case MLX5_MSEES_OPER_STATUS_HOLDOVER:
+		return DPLL_LOCK_STATUS_HOLDOVER;
+	default:
+		return DPLL_LOCK_STATUS_UNLOCKED;
+	}
+}
+
+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;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, NULL, &oper_status);
+	if (err)
+		return err;
+
+	*status = mlx5_dpll_lock_status_from_oper_status(oper_status);
+	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_SOURCE;
+	return 0;
+}
+
+static enum dpll_pin_state
+mlx5_dpll_pin_state_from_admin_status(enum mlx5_msees_admin_status admin_status)
+{
+	return admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK ?
+	       DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
+}
+
+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;
+	struct mlx5_dpll *mdpll = pin_priv;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status, NULL);
+	if (err)
+		return err;
+	*state = mlx5_dpll_pin_state_from_admin_status(admin_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 = {
+	.label = "n/a",
+	.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,
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status);
+	if (err)
+		goto err_out;
+	lock_status = mlx5_dpll_lock_status_from_oper_status(oper_status);
+	pin_state = mlx5_dpll_pin_state_from_admin_status(admin_status);
+
+	if (!mdpll->last.valid)
+		goto invalid_out;
+
+	if (mdpll->last.lock_status != lock_status)
+		dpll_device_notify(mdpll->dpll, DPLL_A_LOCK_STATUS);
+	if (mdpll->last.pin_state != pin_state)
+		dpll_pin_notify(mdpll->dpll, mdpll->dpll_pin, DPLL_A_PIN_STATE);
+
+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, &adev->dev);
+	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, NULL);
+	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 <jiri@nvidia.com>");
+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 a4c4f737f9c1..6e7546f2bb84 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 b42696d74c9f..1742471924d4 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10178,7 +10178,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];
 
@@ -12540,4 +12542,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.34.1


^ permalink raw reply related	[flat|nested] 149+ messages in thread

* [RFC PATCH v7 8/8] mlx5: Implement SyncE support using DPLL infrastructure
@ 2023-04-28  0:20   ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28  0:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

From: Jiri Pirko <jiri@nvidia.com>

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 <jiri@nvidia.com>
---
 .../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    | 438 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 526 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..1469ddbafec7
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,438 @@
+// 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>
+
+// POSSIBLE DPLL API EXTENSIONS:
+// 1) Expose clock quality: MSECQ->local_ssm_code, MSECQ->local_enhanced_ssm_code
+//    Proposed enum:
+//      QL_DNU,
+//      QL_EEC1,
+//      QL_eEEC,
+//      QL_SSU_B,
+//      QL_SSU_A,
+//      QL_PRC,
+//      QL_ePRC,
+//      QL_PRTC,
+//      QL_ePRTC,
+// 2) Expose possibility to do holdover: MSEES->ho_acq
+// 3) DPLL Implementation hw-speficic values (debug?): MSEES->oper_freq_measure
+
+/* 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)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+	int err;
+
+	MLX5_SET(msees_reg, in, local_port, 1);
+	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);
+	if (oper_status)
+		*oper_status = MLX5_GET(msees_reg, out, oper_status);
+	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, local_port, 1);
+	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);
+	MLX5_SET(msees_reg, in, admin_freq_measure,
+		 admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK);
+	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				    MLX5_REG_MSEES, 0, 0);
+}
+
+static enum dpll_lock_status
+mlx5_dpll_lock_status_from_oper_status(enum mlx5_msees_oper_status oper_status)
+{
+	switch (oper_status) {
+	case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
+		return DPLL_LOCK_STATUS_LOCKED;
+	case MLX5_MSEES_OPER_STATUS_HOLDOVER:
+		return DPLL_LOCK_STATUS_HOLDOVER;
+	default:
+		return DPLL_LOCK_STATUS_UNLOCKED;
+	}
+}
+
+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;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, NULL, &oper_status);
+	if (err)
+		return err;
+
+	*status = mlx5_dpll_lock_status_from_oper_status(oper_status);
+	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_SOURCE;
+	return 0;
+}
+
+static enum dpll_pin_state
+mlx5_dpll_pin_state_from_admin_status(enum mlx5_msees_admin_status admin_status)
+{
+	return admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK ?
+	       DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
+}
+
+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;
+	struct mlx5_dpll *mdpll = pin_priv;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status, NULL);
+	if (err)
+		return err;
+	*state = mlx5_dpll_pin_state_from_admin_status(admin_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 = {
+	.label = "n/a",
+	.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,
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status);
+	if (err)
+		goto err_out;
+	lock_status = mlx5_dpll_lock_status_from_oper_status(oper_status);
+	pin_state = mlx5_dpll_pin_state_from_admin_status(admin_status);
+
+	if (!mdpll->last.valid)
+		goto invalid_out;
+
+	if (mdpll->last.lock_status != lock_status)
+		dpll_device_notify(mdpll->dpll, DPLL_A_LOCK_STATUS);
+	if (mdpll->last.pin_state != pin_state)
+		dpll_pin_notify(mdpll->dpll, mdpll->dpll_pin, DPLL_A_PIN_STATE);
+
+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, &adev->dev);
+	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, NULL);
+	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 <jiri@nvidia.com>");
+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 a4c4f737f9c1..6e7546f2bb84 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 b42696d74c9f..1742471924d4 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10178,7 +10178,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];
 
@@ -12540,4 +12542,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.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-04-28  2:36     ` Stephen Hemminger
  -1 siblings, 0 replies; 149+ messages in thread
From: Stephen Hemminger @ 2023-04-28  2:36 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:08 -0700
Vadim Fedorenko <vadfed@meta.com> wrote:

> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
> +	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION

Please don't use C++ style comments

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
@ 2023-04-28  2:36     ` Stephen Hemminger
  0 siblings, 0 replies; 149+ messages in thread
From: Stephen Hemminger @ 2023-04-28  2:36 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:08 -0700
Vadim Fedorenko <vadfed@meta.com> wrote:

> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
> +	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION

Please don't use C++ style comments

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
  (?)
@ 2023-04-28  2:49   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  2:49 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-8-vadfed%40meta.com
patch subject: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20230428/202304281011.Gmu0ofeX-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/896bf6147acb6644a9a3cf4825510821d86ba577
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 896bf6147acb6644a9a3cf4825510821d86ba577
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash net/core/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281011.Gmu0ofeX-lkp@intel.com/

All errors (new ones prefixed by >>):

   net/core/dev.c: In function 'netdev_dpll_pin_assign':
>> net/core/dev.c:8958:12: error: 'struct net_device' has no member named 'dpll_pin'
    8958 |         dev->dpll_pin = dpll_pin;
         |            ^~
--
   net/core/rtnetlink.c: In function 'rtnl_dpll_pin_size':
>> net/core/rtnetlink.c:1056:16: error: 'const struct net_device' has no member named 'dpll_pin'
    1056 |         if (dev->dpll_pin)
         |                ^~
   net/core/rtnetlink.c:1057:53: error: 'const struct net_device' has no member named 'dpll_pin'
    1057 |                 size += dpll_msg_pin_handle_size(dev->dpll_pin);
         |                                                     ^~
   net/core/rtnetlink.c: In function 'rtnl_fill_dpll_pin':
   net/core/rtnetlink.c:1790:16: error: 'const struct net_device' has no member named 'dpll_pin'
    1790 |         if (dev->dpll_pin) {
         |                ^~
   net/core/rtnetlink.c:1791:55: error: 'const struct net_device' has no member named 'dpll_pin'
    1791 |                 ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
         |                                                       ^~


vim +8958 net/core/dev.c

  8954	
  8955	static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin)
  8956	{
  8957		rtnl_lock();
> 8958		dev->dpll_pin = dpll_pin;
  8959		rtnl_unlock();
  8960	}
  8961	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  0:20   ` Vadim Fedorenko
                     ` (2 preceding siblings ...)
  (?)
@ 2023-04-28  3:09   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  3:09 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: llvm, oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-8-vadfed%40meta.com
patch subject: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
config: hexagon-randconfig-r031-20230428 (https://download.01.org/0day-ci/archive/20230428/202304281043.3dUibSbi-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project 437b7602e4a998220871de78afcb020b9c14a661)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/896bf6147acb6644a9a3cf4825510821d86ba577
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 896bf6147acb6644a9a3cf4825510821d86ba577
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash net/core/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281043.3dUibSbi-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from net/core/dev.c:88:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __raw_readb(PCI_IOBASE + addr);
                             ~~~~~~~~~~ ^
   include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu'
   #define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
                                                     ^
   In file included from net/core/dev.c:88:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu'
   #define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
                                                     ^
   In file included from net/core/dev.c:88:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writeb(value, PCI_IOBASE + addr);
                               ~~~~~~~~~~ ^
   include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
>> net/core/dev.c:8958:7: error: no member named 'dpll_pin' in 'struct net_device'
           dev->dpll_pin = dpll_pin;
           ~~~  ^
   6 warnings and 1 error generated.
--
   In file included from net/core/rtnetlink.c:28:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __raw_readb(PCI_IOBASE + addr);
                             ~~~~~~~~~~ ^
   include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu'
   #define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
                                                     ^
   In file included from net/core/rtnetlink.c:28:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu'
   #define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
                                                     ^
   In file included from net/core/rtnetlink.c:28:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writeb(value, PCI_IOBASE + addr);
                               ~~~~~~~~~~ ^
   include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
>> net/core/rtnetlink.c:1056:11: error: no member named 'dpll_pin' in 'struct net_device'
           if (dev->dpll_pin)
               ~~~  ^
   net/core/rtnetlink.c:1057:41: error: no member named 'dpll_pin' in 'struct net_device'
                   size += dpll_msg_pin_handle_size(dev->dpll_pin);
                                                    ~~~  ^
   net/core/rtnetlink.c:1790:11: error: no member named 'dpll_pin' in 'struct net_device'
           if (dev->dpll_pin) {
               ~~~  ^
   net/core/rtnetlink.c:1791:43: error: no member named 'dpll_pin' in 'struct net_device'
                   ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
                                                      ~~~  ^
   6 warnings and 4 errors generated.


vim +8958 net/core/dev.c

  8954	
  8955	static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin)
  8956	{
  8957		rtnl_lock();
> 8958		dev->dpll_pin = dpll_pin;
  8959		rtnl_unlock();
  8960	}
  8961	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
@ 2023-04-28  3:20   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  3:20 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-5-vadfed%40meta.com
patch subject: [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20230428/202304281105.W8gg6UIk-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/13ef3b4ad86b654d71f1e13448b0968c83403d02
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 13ef3b4ad86b654d71f1e13448b0968c83403d02
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 olddefconfig
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/net/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281105.W8gg6UIk-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from include/linux/bits.h:6,
                    from include/linux/bitops.h:6,
                    from include/linux/kernel.h:22,
                    from drivers/net/ethernet/intel/ice/ice.h:9,
                    from drivers/net/ethernet/intel/ice/ice_main.c:9:
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:196:46: error: 'DPLL_PIN_FREQ_SUPP_1_HZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_1_HZ'?
     196 | #define ICE_SIG_TYPE_MASK_1PPS_10MHZ    (BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:197:46: error: 'DPLL_PIN_FREQ_SUPP_10_MHZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_10_MHZ'?
     197 |                                          BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:211:21: error: 'DPLL_PIN_FREQ_SUPP_UNSPEC' undeclared here (not in a function); did you mean 'DPLL_PIN_DIRECTION_UNSPEC'?
     211 |                 BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   In file included from drivers/net/ethernet/intel/ice/ice_ptp.h:10,
                    from drivers/net/ethernet/intel/ice/ice.h:69,
                    from drivers/net/ethernet/intel/ice/ice_main.c:9:
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:325:38: warning: 'ice_e823_zl_cgu_outputs' defined but not used [-Wunused-const-variable=]
     325 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:306:38: warning: 'ice_e823_zl_cgu_inputs' defined but not used [-Wunused-const-variable=]
     306 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:295:38: warning: 'ice_e823_si_cgu_outputs' defined but not used [-Wunused-const-variable=]
     295 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:279:38: warning: 'ice_e823_si_cgu_inputs' defined but not used [-Wunused-const-variable=]
     279 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:262:38: warning: 'ice_e810t_qsfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     262 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:247:38: warning: 'ice_e810t_sfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     247 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:224:38: warning: 'ice_e810t_qsfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     224 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:205:38: warning: 'ice_e810t_sfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     205 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~
--
   In file included from include/linux/bits.h:6,
                    from include/linux/bitops.h:6,
                    from include/linux/kernel.h:22,
                    from drivers/net/ethernet/intel/ice/ice.h:9,
                    from drivers/net/ethernet/intel/ice/ice_lib.c:4:
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:196:46: error: 'DPLL_PIN_FREQ_SUPP_1_HZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_1_HZ'?
     196 | #define ICE_SIG_TYPE_MASK_1PPS_10MHZ    (BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:197:46: error: 'DPLL_PIN_FREQ_SUPP_10_MHZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_10_MHZ'?
     197 |                                          BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:211:21: error: 'DPLL_PIN_FREQ_SUPP_UNSPEC' undeclared here (not in a function); did you mean 'DPLL_PIN_DIRECTION_UNSPEC'?
     211 |                 BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/net/ethernet/intel/ice/ice_lib.c: In function 'ice_init_feature_support':
   drivers/net/ethernet/intel/ice/ice_lib.c:4334:53: error: 'ICE_F_PHY_RCLK' undeclared (first use in this function)
    4334 |                         ice_set_feature_support(pf, ICE_F_PHY_RCLK);
         |                                                     ^~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_lib.c:4334:53: note: each undeclared identifier is reported only once for each function it appears in
   In file included from drivers/net/ethernet/intel/ice/ice_ptp.h:10,
                    from drivers/net/ethernet/intel/ice/ice.h:69,
                    from drivers/net/ethernet/intel/ice/ice_lib.c:4:
   At top level:
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:325:38: warning: 'ice_e823_zl_cgu_outputs' defined but not used [-Wunused-const-variable=]
     325 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:306:38: warning: 'ice_e823_zl_cgu_inputs' defined but not used [-Wunused-const-variable=]
     306 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:295:38: warning: 'ice_e823_si_cgu_outputs' defined but not used [-Wunused-const-variable=]
     295 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:279:38: warning: 'ice_e823_si_cgu_inputs' defined but not used [-Wunused-const-variable=]
     279 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:262:38: warning: 'ice_e810t_qsfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     262 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:247:38: warning: 'ice_e810t_sfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     247 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:224:38: warning: 'ice_e810t_qsfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     224 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h:205:38: warning: 'ice_e810t_sfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     205 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~


vim +/ice_e823_zl_cgu_outputs +325 drivers/net/ethernet/intel/ice/ice_ptp_hw.h

   204	
 > 205	static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
   206		{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
   207			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   208		{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   209			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   210		{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
   211			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   212		{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
   213			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   214		{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
   215			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   216		{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
   217			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   218		{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
   219			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   220		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   221			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   222	};
   223	
 > 224	static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
   225		{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
   226			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   227		{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   228			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   229		{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
   230			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   231		{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
   232			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   233		{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
   234			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   235		{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
   236			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   237		{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
   238			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   239		{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
   240			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   241		{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
   242			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   243		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   244				BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   245	};
   246	
 > 247	static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
   248		{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
   249			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   250		{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
   251			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   252		{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   253			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   254		{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   255			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   256		{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
   257			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   258		{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
   259			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   260	};
   261	
 > 262	static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
   263		{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
   264			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   265		{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
   266			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   267		{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   268			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   269		{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   270			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   271		{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   272			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   273		{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
   274			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   275		{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
   276			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   277	};
   278	
 > 279	static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
   280		{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
   281		{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
   282		{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
   283			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   284		{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
   285			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   286		{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
   287			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   288		{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
   289		{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
   290			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   291		{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
   292			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   293	};
   294	
 > 295	static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
   296		{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
   297			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   298		{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   299			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   300		{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
   301			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   302		{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
   303			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   304	};
   305	
 > 306	static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
   307		{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
   308		{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
   309			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   310		{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
   311			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   312		{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
   313			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   314		{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
   315		{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
   316		{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
   317			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   318		{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
   319		{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
   320			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   321		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   322				BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   323	};
   324	
 > 325	static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
   326		{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
   327			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   328		{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
   329			BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
   330		{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   331			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   332		{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   333			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   334		{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
   335			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   336		{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
   337	};
   338	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
@ 2023-04-28  6:05   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  6:05 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-6-vadfed%40meta.com
patch subject: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
config: x86_64-randconfig-a002 (https://download.01.org/0day-ci/archive/20230428/202304281454.5Y9tc47p-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/08659ac96f7a68ef1966cf8784cfcfbe8cc96370
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 08659ac96f7a68ef1966cf8784cfcfbe8cc96370
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 olddefconfig
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/dpll/ drivers/net/ethernet/intel/ice/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281454.5Y9tc47p-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/dpll/dpll_core.c:296:1: warning: no previous prototype for 'dpll_xa_ref_dpll_find' [-Wmissing-prototypes]
     296 | dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
         | ^~~~~~~~~~~~~~~~~~~~~
--
>> drivers/dpll/dpll_netlink.c:969:13: warning: no previous prototype for 'dpll_netlink_fini' [-Wmissing-prototypes]
     969 | void __exit dpll_netlink_fini(void)
         |             ^~~~~~~~~~~~~~~~~
--
>> drivers/net/ethernet/intel/ice/ice_dpll.c:486:1: warning: no previous prototype for 'ice_dpll_pin_state_update' [-Wmissing-prototypes]
     486 | ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
         | ^~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/net/ethernet/intel/ice/ice_ptp.h:10,
                    from drivers/net/ethernet/intel/ice/ice.h:69,
                    from drivers/net/ethernet/intel/ice/ice_dpll.c:4:
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:317:38: warning: 'ice_e823_zl_cgu_outputs' defined but not used [-Wunused-const-variable=]
     317 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:301:38: warning: 'ice_e823_zl_cgu_inputs' defined but not used [-Wunused-const-variable=]
     301 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:291:38: warning: 'ice_e823_si_cgu_outputs' defined but not used [-Wunused-const-variable=]
     291 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:277:38: warning: 'ice_e823_si_cgu_inputs' defined but not used [-Wunused-const-variable=]
     277 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:263:38: warning: 'ice_e810t_qsfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     263 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:250:38: warning: 'ice_e810t_sfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     250 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:232:38: warning: 'ice_e810t_qsfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     232 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h:216:38: warning: 'ice_e810t_sfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     216 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~
--
   drivers/net/ethernet/intel/ice/ice_dpll.c:18: warning: cannot understand function prototype: 'const enum dpll_lock_status ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = '
   drivers/net/ethernet/intel/ice/ice_dpll.c:29: warning: cannot understand function prototype: 'enum ice_dpll_pin_type '
   drivers/net/ethernet/intel/ice/ice_dpll.c:39: warning: cannot understand function prototype: 'const char * const pin_type_name[] = '
   drivers/net/ethernet/intel/ice/ice_dpll.c:327: warning: Excess function parameter 'dpll_priv' description in 'ice_dpll_frequency_get'
   drivers/net/ethernet/intel/ice/ice_dpll.c:488: warning: Function parameter or member 'pf' not described in 'ice_dpll_pin_state_update'
   drivers/net/ethernet/intel/ice/ice_dpll.c:488: warning: Excess function parameter 'hw' description in 'ice_dpll_pin_state_update'
   drivers/net/ethernet/intel/ice/ice_dpll.c:613: warning: Function parameter or member 'priv' not described in 'ice_dpll_lock_status_get'
   drivers/net/ethernet/intel/ice/ice_dpll.c:613: warning: Function parameter or member 'extack' not described in 'ice_dpll_lock_status_get'
>> drivers/net/ethernet/intel/ice/ice_dpll.c:681: warning: expecting prototype for ice_dpll_mode_get(). Prototype was for ice_dpll_mode_supported() instead
>> drivers/net/ethernet/intel/ice/ice_dpll.c:1068: warning: expecting prototype for ice_dpll_source_direction(). Prototype was for ice_dpll_output_direction() instead
   drivers/net/ethernet/intel/ice/ice_dpll.c:1094: warning: Excess function parameter 'dpll' description in 'ice_dpll_rclk_state_on_pin_set'
   drivers/net/ethernet/intel/ice/ice_dpll.c:1467: warning: Function parameter or member 'init' not described in 'ice_dpll_update_state'
   drivers/net/ethernet/intel/ice/ice_dpll.c:1756: warning: Excess function parameter 'pin_type' description in 'ice_dpll_init_rclk_pin'


vim +/ice_dpll_pin_state_update +486 drivers/net/ethernet/intel/ice/ice_dpll.c

   470	
   471	/**
   472	 * ice_dpll_pin_state_update - update pin's state
   473	 * @hw: private board struct
   474	 * @pin: structure with pin attributes to be updated
   475	 * @pin_type: type of pin being updated
   476	 *
   477	 * Determine pin current state and frequency, then update struct
   478	 * holding the pin info. For source pin states are separated for each
   479	 * dpll, for rclk pins states are separated for each parent.
   480	 *
   481	 * Return:
   482	 * * 0 - OK
   483	 * * negative - error
   484	 */
   485	int
 > 486	ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
   487				  const enum ice_dpll_pin_type pin_type)
   488	{
   489		int ret;
   490	
   491		if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
   492			ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
   493						       NULL, &pin->flags[0],
   494						       &pin->freq, NULL);
   495			if (!!(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0])) {
   496				if (pin->pin) {
   497					pin->state[pf->dplls.eec.dpll_idx] =
   498						pin->pin == pf->dplls.eec.active_source ?
   499						DPLL_PIN_STATE_CONNECTED :
   500						DPLL_PIN_STATE_SELECTABLE;
   501					pin->state[pf->dplls.pps.dpll_idx] =
   502						pin->pin == pf->dplls.pps.active_source ?
   503						DPLL_PIN_STATE_CONNECTED :
   504						DPLL_PIN_STATE_SELECTABLE;
   505				} else {
   506					pin->state[pf->dplls.eec.dpll_idx] =
   507						DPLL_PIN_STATE_SELECTABLE;
   508					pin->state[pf->dplls.pps.dpll_idx] =
   509						DPLL_PIN_STATE_SELECTABLE;
   510				}
   511			} else {
   512				pin->state[pf->dplls.eec.dpll_idx] =
   513					DPLL_PIN_STATE_DISCONNECTED;
   514				pin->state[pf->dplls.pps.dpll_idx] =
   515					DPLL_PIN_STATE_DISCONNECTED;
   516			}
   517		} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
   518			ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
   519							&pin->flags[0], NULL,
   520							&pin->freq, NULL);
   521			if (!!(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0]))
   522				pin->state[0] = DPLL_PIN_STATE_CONNECTED;
   523			else
   524				pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
   525		} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
   526			u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
   527	
   528			for (parent = 0; parent < pf->dplls.rclk.num_parents;
   529			     parent++) {
   530				ret = ice_aq_get_phy_rec_clk_out(&pf->hw, parent,
   531								 &port_num,
   532								 &pin->flags[parent],
   533								 &pin->freq);
   534				if (ret)
   535					return ret;
   536				if (!!(ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
   537				       pin->flags[parent]))
   538					pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
   539				else
   540					pin->state[parent] =
   541						DPLL_PIN_STATE_DISCONNECTED;
   542			}
   543		}
   544	
   545		return ret;
   546	}
   547	
   548	/**
   549	 * ice_find_dpll - find ice_dpll on a pf
   550	 * @pf: private board structure
   551	 * @dpll: kernel's dpll_device pointer to be searched
   552	 *
   553	 * Return:
   554	 * * pointer if ice_dpll with given device dpll pointer is found
   555	 * * NULL if not found
   556	 */
   557	static struct ice_dpll
   558	*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
   559	{
   560		if (!pf || !dpll)
   561			return NULL;
   562	
   563		return dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
   564		       dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL;
   565	}
   566	
   567	/**
   568	 * ice_dpll_hw_source_prio_set - set source priority value in hardware
   569	 * @pf: board private structure
   570	 * @dpll: ice dpll pointer
   571	 * @pin: ice pin pointer
   572	 * @prio: priority value being set on a dpll
   573	 *
   574	 * Internal wrapper for setting the priority in the hardware.
   575	 *
   576	 * Return:
   577	 * * 0 - success
   578	 * * negative - failure
   579	 */
   580	static int
   581	ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
   582				    struct ice_dpll_pin *pin, const u32 prio)
   583	{
   584		int ret;
   585	
   586		ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
   587					      (u8)prio);
   588		if (ret)
   589			dev_dbg(ice_pf_to_dev(pf),
   590				"err:%d %s failed to set pin prio:%u on pin:%u\n",
   591				ret, ice_aq_str(pf->hw.adminq.sq_last_status),
   592				prio, pin->idx);
   593		else
   594			dpll->input_prio[pin->idx] = prio;
   595	
   596		return ret;
   597	}
   598	
   599	/**
   600	 * ice_dpll_lock_status_get - get dpll lock status callback
   601	 * @dpll: registered dpll pointer
   602	 * @status: on success holds dpll's lock status
   603	 *
   604	 * Dpll subsystem callback, provides dpll's lock status.
   605	 *
   606	 * Return:
   607	 * * 0 - success
   608	 * * negative - failure
   609	 */
   610	static int ice_dpll_lock_status_get(const struct dpll_device *dpll, void *priv,
   611					    enum dpll_lock_status *status,
   612					    struct netlink_ext_ack *extack)
   613	{
   614		struct ice_pf *pf = priv;
   615		struct ice_dpll *d;
   616	
   617		if (!pf)
   618			return -EINVAL;
   619		if (ice_dpll_cb_lock(pf))
   620			return -EBUSY;
   621		d = ice_find_dpll(pf, dpll);
   622		if (!d)
   623			return -EFAULT;
   624		dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
   625		*status = ice_dpll_status[d->dpll_state];
   626		ice_dpll_cb_unlock(pf);
   627	
   628		return 0;
   629	}
   630	
   631	/**
   632	 * ice_dpll_mode_get - get dpll's working mode
   633	 * @dpll: registered dpll pointer
   634	 * @priv: private data pointer passed on dpll registration
   635	 * @mode: on success holds current working mode of dpll
   636	 * @extack: error reporting
   637	 *
   638	 * Dpll subsystem callback. Provides working mode of dpll.
   639	 *
   640	 * Return:
   641	 * * 0 - success
   642	 * * negative - failure
   643	 */
   644	static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
   645				     enum dpll_mode *mode,
   646				     struct netlink_ext_ack *extack)
   647	{
   648		struct ice_pf *pf = priv;
   649		struct ice_dpll *d;
   650	
   651		if (!pf)
   652			return -EINVAL;
   653		if (ice_dpll_cb_lock(pf))
   654			return -EBUSY;
   655		d = ice_find_dpll(pf, dpll);
   656		ice_dpll_cb_unlock(pf);
   657		if (!d)
   658			return -EFAULT;
   659		*mode = DPLL_MODE_AUTOMATIC;
   660	
   661		return 0;
   662	}
   663	
   664	/**
   665	 * ice_dpll_mode_get - check if dpll's working mode is supported
   666	 * @dpll: registered dpll pointer
   667	 * @priv: private data pointer passed on dpll registration
   668	 * @mode: mode to be checked for support
   669	 * @extack: error reporting
   670	 *
   671	 * Dpll subsystem callback. Provides information if working mode is supported
   672	 * by dpll.
   673	 *
   674	 * Return:
   675	 * * true - mode is supported
   676	 * * false - mode is not supported
   677	 */
   678	static bool ice_dpll_mode_supported(const struct dpll_device *dpll, void *priv,
   679					    const enum dpll_mode mode,
   680					    struct netlink_ext_ack *extack)
 > 681	{
   682		struct ice_pf *pf = priv;
   683		struct ice_dpll *d;
   684	
   685		if (!pf)
   686			return false;
   687	
   688		if (ice_dpll_cb_lock(pf))
   689			return false;
   690		d = ice_find_dpll(pf, dpll);
   691		ice_dpll_cb_unlock(pf);
   692		if (!d)
   693			return false;
   694		if (mode == DPLL_MODE_AUTOMATIC)
   695			return true;
   696	
   697		return false;
   698	}
   699	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
@ 2023-04-28  6:36   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  6:36 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: llvm, oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-7-vadfed%40meta.com
patch subject: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
config: x86_64-randconfig-a005 (https://download.01.org/0day-ci/archive/20230428/202304281438.QAdSihGx-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/45d9112b4633e7cea8984cb09cc5f55203a8ec32
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 45d9112b4633e7cea8984cb09cc5f55203a8ec32
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/dpll/ drivers/ptp/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281438.QAdSihGx-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/dpll/dpll_core.c:296:1: warning: no previous prototype for function 'dpll_xa_ref_dpll_find' [-Wmissing-prototypes]
   dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
   ^
   drivers/dpll/dpll_core.c:295:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   struct dpll_pin_ref *
   ^
   static 
   1 warning generated.
--
>> drivers/dpll/dpll_netlink.c:969:13: warning: no previous prototype for function 'dpll_netlink_fini' [-Wmissing-prototypes]
   void __exit dpll_netlink_fini(void)
               ^
   drivers/dpll/dpll_netlink.c:969:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   void __exit dpll_netlink_fini(void)
   ^
   static 
   1 warning generated.
--
>> drivers/ptp/ptp_ocp.c:4314:37: warning: variable 'val' is uninitialized when used here [-Wuninitialized]
                           return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
                                                            ^~~
   drivers/ptp/ptp_ocp.c:4306:9: note: initialize the variable 'val' to silence this warning
           int val, i;
                  ^
                   = 0
   1 warning generated.


vim +/val +4314 drivers/ptp/ptp_ocp.c

  4295	
  4296	static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
  4297					      void *pin_priv,
  4298					      const struct dpll_device *dpll,
  4299					      void *dpll_priv, u64 frequency,
  4300					      struct netlink_ext_ack *extack)
  4301	{
  4302		struct ptp_ocp_sma_connector *sma = pin_priv;
  4303		struct ptp_ocp *bp = dpll_priv;
  4304		const struct ocp_selector *tbl;
  4305		int sma_nr = (sma - bp->sma);
  4306		int val, i;
  4307	
  4308		if (sma->fixed_fcn)
  4309			return -EOPNOTSUPP;
  4310	
  4311		tbl = bp->sma_op->tbl[sma->mode];
  4312		for (i = 0; tbl[i].name; i++)
  4313			if (tbl[i].frequency == frequency)
> 4314				return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
  4315		return -EINVAL;
  4316	}
  4317	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
  (?)
@ 2023-04-28  8:18   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28  8:18 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: llvm, oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-6-vadfed%40meta.com
patch subject: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
config: i386-randconfig-a013 (https://download.01.org/0day-ci/archive/20230428/202304281647.9vCSasdW-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/08659ac96f7a68ef1966cf8784cfcfbe8cc96370
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 08659ac96f7a68ef1966cf8784cfcfbe8cc96370
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304281647.9vCSasdW-lkp@intel.com/

All error/warnings (new ones prefixed by >>, old ones prefixed by <<):

>> drivers/net/ethernet/intel/ice/ice_dpll.c:156:7: warning: variable 'pin_num' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
if (pin == pf->dplls.rclk.pin)
^~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:162:18: note: uninitialized use occurs here
for (i = 0; i < pin_num; i++)
^~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:156:3: note: remove the 'if' if its condition is always true
if (pin == pf->dplls.rclk.pin)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:144:13: note: initialize the variable 'pin_num' to silence this warning
int pin_num, i;
^
= 0
>> drivers/net/ethernet/intel/ice/ice_dpll.c:419:13: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:423:6: note: uninitialized use occurs here
if (ret)
^~~
drivers/net/ethernet/intel/ice/ice_dpll.c:419:9: note: remove the 'if' if its condition is always true
} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:414:9: note: initialize the variable 'ret' to silence this warning
int ret;
^
= 0
drivers/net/ethernet/intel/ice/ice_dpll.c:456:13: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:460:6: note: uninitialized use occurs here
if (ret)
^~~
drivers/net/ethernet/intel/ice/ice_dpll.c:456:9: note: remove the 'if' if its condition is always true
} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:451:9: note: initialize the variable 'ret' to silence this warning
int ret;
^
= 0
>> drivers/net/ethernet/intel/ice/ice_dpll.c:486:1: warning: no previous prototype for function 'ice_dpll_pin_state_update' [-Wmissing-prototypes]
ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
^
drivers/net/ethernet/intel/ice/ice_dpll.c:485:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int
^
static
drivers/net/ethernet/intel/ice/ice_dpll.c:525:13: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:545:9: note: uninitialized use occurs here
return ret;
^~~
drivers/net/ethernet/intel/ice/ice_dpll.c:525:9: note: remove the 'if' if its condition is always true
} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/intel/ice/ice_dpll.c:489:9: note: initialize the variable 'ret' to silence this warning
int ret;
^
= 0
>> drivers/net/ethernet/intel/ice/ice_dpll.c:1487:45: warning: overlapping comparisons always evaluate to false [-Wtautological-overlap-compare]
if (d->dpll_state == ICE_CGU_STATE_LOCKED &&
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
6 warnings generated.
--
>> ERROR: modpost: "__divdi3" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_is_phy_rclk_present" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_is_cgu_present" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_is_clock_mux_present_e810t" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_get_cgu_rclk_pin_info" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_get_cgu_state" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_cgu_get_pin_name" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_cgu_get_pin_type" [drivers/net/ethernet/intel/ice/ice.ko] undefined!
>> ERROR: modpost: "ice_cgu_get_pin_freq_supp" [drivers/net/ethernet/intel/ice/ice.ko] undefined!

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  2:36     ` Stephen Hemminger
@ 2023-04-28 10:00       ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28 10:00 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

On 28/04/2023 03:36, Stephen Hemminger wrote:
> On Thu, 27 Apr 2023 17:20:08 -0700
> Vadim Fedorenko <vadfed@meta.com> wrote:
> 
>> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
>> +{
>> +	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
>> +	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
> 
> Please don't use C++ style comments

Sure, these comments were put in as a placeholder for RFC patches, will
be definitely removed for the next version which I hope will be ready to
merge.


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
@ 2023-04-28 10:00       ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-04-28 10:00 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

On 28/04/2023 03:36, Stephen Hemminger wrote:
> On Thu, 27 Apr 2023 17:20:08 -0700
> Vadim Fedorenko <vadfed@meta.com> wrote:
> 
>> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
>> +{
>> +	// TMP- THE HANDLE IS GOING TO CHANGE TO DRIVERNAME/CLOCKID/PIN_INDEX
>> +	// LEAVING ORIG HANDLE NOW AS PUT IN THE LAST RFC VERSION
> 
> Please don't use C++ style comments

Sure, these comments were put in as a placeholder for RFC patches, will
be definitely removed for the next version which I hope will be ready to
merge.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
  (?)
@ 2023-04-28 17:53   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-04-28 17:53 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230427]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-5-vadfed%40meta.com
patch subject: [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230429/202304290110.EXkC5dHd-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/13ef3b4ad86b654d71f1e13448b0968c83403d02
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout 13ef3b4ad86b654d71f1e13448b0968c83403d02
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=i386 olddefconfig
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304290110.EXkC5dHd-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from include/linux/bits.h:6,
                    from include/linux/bitops.h:6,
                    from include/linux/kernel.h:22,
                    from include/linux/skbuff.h:13,
                    from include/linux/ip.h:16,
                    from drivers/infiniband/hw/irdma/main.h:6,
                    from drivers/infiniband/hw/irdma/main.c:3:
   drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:196:46: error: 'DPLL_PIN_FREQ_SUPP_1_HZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_1_HZ'?
     196 | #define ICE_SIG_TYPE_MASK_1PPS_10MHZ    (BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:197:46: error: 'DPLL_PIN_FREQ_SUPP_10_MHZ' undeclared here (not in a function); did you mean 'DPLL_PIN_FREQUENCY_10_MHZ'?
     197 |                                          BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
         |                                              ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:207:17: note: in expansion of macro 'ICE_SIG_TYPE_MASK_1PPS_10MHZ'
     207 |                 ICE_SIG_TYPE_MASK_1PPS_10MHZ },
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:211:21: error: 'DPLL_PIN_FREQ_SUPP_UNSPEC' undeclared here (not in a function); did you mean 'DPLL_PIN_DIRECTION_UNSPEC'?
     211 |                 BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/vdso/bits.h:7:44: note: in definition of macro 'BIT'
       7 | #define BIT(nr)                 (UL(1) << (nr))
         |                                            ^~
   In file included from drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp.h:10,
                    from drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice.h:69,
                    from drivers/infiniband/hw/irdma/main.c:4:
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:325:38: warning: 'ice_e823_zl_cgu_outputs' defined but not used [-Wunused-const-variable=]
     325 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:306:38: warning: 'ice_e823_zl_cgu_inputs' defined but not used [-Wunused-const-variable=]
     306 | static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:295:38: warning: 'ice_e823_si_cgu_outputs' defined but not used [-Wunused-const-variable=]
     295 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:279:38: warning: 'ice_e823_si_cgu_inputs' defined but not used [-Wunused-const-variable=]
     279 | static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:262:38: warning: 'ice_e810t_qsfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     262 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:247:38: warning: 'ice_e810t_sfp_cgu_outputs' defined but not used [-Wunused-const-variable=]
     247 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:224:38: warning: 'ice_e810t_qsfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     224 | static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h:205:38: warning: 'ice_e810t_sfp_cgu_inputs' defined but not used [-Wunused-const-variable=]
     205 | static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
         |                                      ^~~~~~~~~~~~~~~~~~~~~~~~


vim +/ice_e823_zl_cgu_outputs +325 drivers/infiniband/hw/irdma/../../../net/ethernet/intel/ice/ice_ptp_hw.h

   204	
 > 205	static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
   206		{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
   207			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   208		{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   209			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   210		{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
   211			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   212		{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
   213			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   214		{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
   215			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   216		{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
   217			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   218		{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
   219			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   220		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   221			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   222	};
   223	
 > 224	static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
   225		{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
   226			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   227		{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   228			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   229		{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
   230			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   231		{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
   232			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   233		{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
   234			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   235		{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
   236			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   237		{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
   238			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   239		{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
   240			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   241		{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
   242			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   243		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   244				BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   245	};
   246	
 > 247	static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
   248		{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
   249			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   250		{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
   251			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   252		{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   253			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   254		{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   255			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   256		{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
   257			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   258		{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
   259			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   260	};
   261	
 > 262	static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
   263		{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
   264			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   265		{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
   266			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   267		{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   268			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   269		{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   270			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   271		{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   272			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   273		{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
   274			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   275		{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
   276			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   277	};
   278	
 > 279	static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
   280		{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
   281		{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
   282		{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
   283			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   284		{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
   285			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   286		{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
   287			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   288		{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
   289		{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
   290			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   291		{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
   292			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   293	};
   294	
 > 295	static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
   296		{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
   297			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   298		{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   299			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   300		{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
   301			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   302		{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
   303			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   304	};
   305	
 > 306	static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
   307		{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
   308		{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
   309			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   310		{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
   311			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   312		{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
   313			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   314		{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
   315		{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
   316		{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
   317			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   318		{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
   319		{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
   320			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   321		{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
   322				BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   323	};
   324	
 > 325	static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
   326		{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
   327			BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
   328		{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
   329			BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
   330		{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   331			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   332		{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
   333			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
   334		{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
   335			ICE_SIG_TYPE_MASK_1PPS_10MHZ },
   336		{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
   337	};
   338	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-04-28  0:20   ` Vadim Fedorenko
  (?)
@ 2023-05-02  7:50   ` kernel test robot
  -1 siblings, 0 replies; 149+ messages in thread
From: kernel test robot @ 2023-05-02  7:50 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.3]
[cannot apply to tnguy-next-queue/dev-queue linus/master next-20230428]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
base:   457391b0380335d5e9a5babdec90ac53928b23b4
patch link:    https://lore.kernel.org/r/20230428002009.2948020-4-vadfed%40meta.com
patch subject: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
reproduce:
        # https://github.com/intel-lab-lkp/linux/commit/fe02747a2a3103b5309f4a669980927ed01cc1dd
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/dpll-spec-Add-Netlink-spec-in-YAML/20230428-082340
        git checkout fe02747a2a3103b5309f4a669980927ed01cc1dd
        make menuconfig
        # enable CONFIG_COMPILE_TEST, CONFIG_WARN_MISSING_DOCUMENTS, CONFIG_WARN_ABI_ERRORS
        make htmldocs

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202305021532.Z2jjjmzh-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> Documentation/dpll.rst:60: WARNING: Unexpected indentation.
>> Documentation/dpll.rst:66: WARNING: Block quote ends without a blank line; unexpected unindent.
>> Documentation/dpll.rst:68: WARNING: Bullet list ends without a blank line; unexpected unindent.
>> Documentation/dpll.rst:108: WARNING: Inline literal start-string without end-string.
>> Documentation/dpll.rst:101: WARNING: Definition list ends without a blank line; unexpected unindent.
>> Documentation/dpll.rst:153: WARNING: Malformed table.
>> Documentation/networking/index.rst:8: WARNING: toctree contains reference to nonexisting document 'networking/dpll'
>> Documentation/dpll.rst: WARNING: document isn't included in any toctree

vim +60 Documentation/dpll.rst

    55	
    56	In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
    57	one of following pin states:
    58	- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
    59	- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
  > 60	  device
    61	
    62	In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
    63	receive one of following pin states:
    64	- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
    65	  source for automatic selection algorithm
  > 66	- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
    67	  a valid source for automatic selection algorithm
  > 68	In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
    69	pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
    70	algorithm locks a dpll device with one of the sources.
    71	
    72	
    73	Shared pins
    74	===========
    75	A single pin object can be registered to multiple dpll devices.
    76	Then there are two groups of configuration knobs:
    77	1) Set on a pin - the configuration affects all dpll devices pin is
    78	   registered to. (i.e. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
    79	2) Set on a pin-dpll tuple - the configuration affects only selected
    80	   dpll device. (i.e. PIN_PRIO, PIN_STATE).
    81	
    82	
    83	MUX-type pins
    84	=============
    85	A pin can be MUX-type, it aggregates child pins and serves as a pin
    86	multiplexer. One or more pins are registered with MUX-type instead of
    87	being directly registered to a dpll device.
    88	Pins registered with a MUX-type provide user with additional nested
    89	attribute ``DPLL_A_PIN_PARENT`` for each parent they were registered
    90	with.
    91	If a pin was registered with multiple parent pins, they behave like a
    92	multiple output multiplexer. In this case output of a
    93	``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
    94	attributes with current state related to each parent, like:
    95	
    96	``'pin': [{
    97	        'device': [{'bus-name': 'pci',
    98	                    'dev-name': '0000:21:00.0_0', 'id': 0}],
    99	        'pin-direction': {'doc': 'pin used as a source of a signal',
   100	                          'name': 'source'},
 > 101	        'pin-idx': 13,
   102	        'pin-parent': [{'pin-parent-idx': 2,
   103	                        'pin-state': {'doc': 'pin disconnected',
   104	                                      'name': 'disconnected'}},
   105	                       {'pin-parent-idx': 3,
   106	                        'pin-state': {'doc': 'pin disconnected',
   107	                                      'name': 'disconnected'}}],
 > 108	        }]``
   109	
   110	Only one child pin can provide it's signal to the parent MUX-type pin at
   111	a time, the selection is done with requesting change of child pin state
   112	to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
   113	index value in ``DPLL_A_PARENT_PIN_IDX``.
   114	
   115	Pin priority
   116	============
   117	Some devices might offer a capability of automatic pin selection mode
   118	(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
   119	Usually such automatic selection is offloaded to the hardware,
   120	which means only pins directly connected to the dpll are capable of
   121	automatic source pin selection.
   122	In automatic selection mode, the user cannot manually select a source
   123	pin for the device, instead the user shall provide all directly
   124	connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
   125	pick a highest priority valid signal and connect with it.
   126	Child pin of MUX-type is not capable of automatic source pin selection,
   127	in order to configure a source of a MUX-type pin, the user needs to
   128	request desired pin state of the child pin on the parent - it is done
   129	with providing additional attribute for pin set state request - index
   130	of parent pin he wish to propagate its signal to
   131	(``DPLL_A_PARENT_PIN_IDX``).
   132	
   133	
   134	Configuration commands group
   135	============================
   136	
   137	Configuration commands are used to get or dump information about
   138	registered dpll devices (and pins), as well as set configuration of
   139	device or pins. As dpll device could not be abstract and reflects real
   140	hardware, there is no way to add new dpll device via netlink from user
   141	space and each device should be registered by it's driver.
   142	
   143	All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
   144	any spamming/D.o.S. from unauthorized userspace applications.
   145	
   146	List of netlink commands with possible attributes
   147	=================================================
   148	
   149	All constants identifying command types use ``DPLL_CMD_`` prefix and
   150	suffix according to command purpose. All attributes use ``DPLL_A_``
   151	prefix and suffix according to attribute purpose:
   152	
 > 153	  ============================  =======================================
   154	  ``DEVICE_GET``                command to get device info or dump list
   155	                                of available devices
   156	    ``ID``                      attr internal dpll device ID
   157	    ``DEV_NAME``                attr dpll device name
   158	    ``BUS_NAME``                attr dpll device bus name
   159	    ``MODE``                    attr selection mode
   160	    ``MODE_SUPPORTED``          attr available selection modes
   161	    ``LOCK_STATUS``             attr internal frequency-lock status
   162	    ``TEMP``                    attr device temperature information
   163	    ``CLOCK_ID``                attr Unique Clock Identifier (EUI-64),
   164	                                as defined by the IEEE 1588 standard
   165	    ``TYPE``                    attr type or purpose of dpll device
   166	  ``DEVICE_SET``                command to set dpll device configuration
   167	    ``ID``                      attr internal dpll device index
   168	    ``NAME``                    attr dpll device name (not required if
   169	                                dpll device index was provided)
   170	    ``MODE``                    attr selection mode to configure
   171	  ``PIN_GET``                   command to get pin info or dump list of
   172	                                available pins
   173	    ``DEVICE``                  nest attr for each dpll device pin is
   174	                                connected with
   175	      ``ID``                    attr internal dpll device ID
   176	      ``DEV_NAME``              attr dpll device name
   177	      ``BUS_NAME``              attr dpll device bus name
   178	      ``PIN_PRIO``              attr priority of pin on the dpll device
   179	      ``PIN_STATE``             attr state of pin on the dpll device
   180	    ``PIN_IDX``                 attr index of a pin on the dpll device
   181	    ``PIN_DESCRIPTION``         attr description provided by driver
   182	    ``PIN_TYPE``                attr type of a pin
   183	    ``PIN_DIRECTION``           attr direction of a pin
   184	    ``PIN_FREQUENCY``           attr current frequency of a pin
   185	    ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
   186	    ``PIN_ANY_FREQUENCY_MIN``   attr minimum value of frequency in case
   187	                                pin/dpll supports any frequency
   188	    ``PIN_ANY_FREQUENCY_MAX``   attr maximum value of frequency in case
   189	                                pin/dpll supports any frequency
   190	    ``PIN_PARENT``              nest attr for each MUX-type parent, that
   191	                                pin is connected with
   192	      ``PIN_PARENT_IDX``        attr index of a parent pin on the dpll
   193	                                device
   194	      ``PIN_STATE``             attr state of a pin on parent pin
   195	    ``PIN_RCLK_DEVICE``         attr name of a device, where pin
   196	                                recovers clock signal from
   197	    ``PIN_DPLL_CAPS``           attr bitmask of pin-dpll capabilities
   198	
   199	  ``PIN_SET``                   command to set pins configuration
   200	    ``ID``                      attr internal dpll device index
   201	    ``BUS_NAME``                attr dpll device name (not required if
   202	                                dpll device ID was provided)
   203	    ``DEV_NAME``                attr dpll device name (not required if
   204	                                dpll device ID was provided)
   205	    ``PIN_IDX``                 attr index of a pin on the dpll device
   206	    ``PIN_DIRECTION``           attr direction to be set
   207	    ``PIN_FREQUENCY``           attr frequency to be set
   208	    ``PIN_PRIO``                attr pin priority to be set
   209	    ``PIN_STATE``               attr pin state to be set
   210	    ``PIN_PARENT_IDX``          attr if provided state is to be set with
   211	                                parent pin instead of with dpll device
   212	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-05-02  8:55   ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02  8:55 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>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.

Vadim, I guess you forgot, could you please add some example commands
here? Please keep them in for the next V.

Thanks!


>
>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 (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/dpll.rst                        |  408 ++++
> Documentation/netlink/specs/dpll.yaml         |  472 ++++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    8 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |   10 +
> drivers/dpll/dpll_core.c                      |  939 ++++++++
> drivers/dpll/dpll_core.h                      |  113 +
> drivers/dpll/dpll_netlink.c                   |  991 +++++++++
> drivers/dpll/dpll_netlink.h                   |   27 +
> drivers/dpll/dpll_nl.c                        |  126 ++
> drivers/dpll/dpll_nl.h                        |   42 +
> 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     | 1929 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |  101 +
> 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    |  438 ++++
> drivers/ptp/Kconfig                           |    1 +
> drivers/ptp/ptp_ocp.c                         |  327 ++-
> include/linux/dpll.h                          |  294 +++
> include/linux/mlx5/driver.h                   |    2 +
> include/linux/mlx5/mlx5_ifc.h                 |   59 +-
> include/linux/netdevice.h                     |    7 +
> include/uapi/linux/dpll.h                     |  204 ++
> include/uapi/linux/if_link.h                  |    2 +
> net/core/dev.c                                |   20 +
> net/core/rtnetlink.c                          |   38 +
> 41 files changed, 7966 insertions(+), 59 deletions(-)
> create mode 100644 Documentation/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.34.1
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-02  8:55   ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02  8:55 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>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.

Vadim, I guess you forgot, could you please add some example commands
here? Please keep them in for the next V.

Thanks!


>
>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 (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/dpll.rst                        |  408 ++++
> Documentation/netlink/specs/dpll.yaml         |  472 ++++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    8 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |   10 +
> drivers/dpll/dpll_core.c                      |  939 ++++++++
> drivers/dpll/dpll_core.h                      |  113 +
> drivers/dpll/dpll_netlink.c                   |  991 +++++++++
> drivers/dpll/dpll_netlink.h                   |   27 +
> drivers/dpll/dpll_nl.c                        |  126 ++
> drivers/dpll/dpll_nl.h                        |   42 +
> 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     | 1929 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |  101 +
> 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    |  438 ++++
> drivers/ptp/Kconfig                           |    1 +
> drivers/ptp/ptp_ocp.c                         |  327 ++-
> include/linux/dpll.h                          |  294 +++
> include/linux/mlx5/driver.h                   |    2 +
> include/linux/mlx5/mlx5_ifc.h                 |   59 +-
> include/linux/netdevice.h                     |    7 +
> include/uapi/linux/dpll.h                     |  204 ++
> include/uapi/linux/if_link.h                  |    2 +
> net/core/dev.c                                |   20 +
> net/core/rtnetlink.c                          |   38 +
> 41 files changed, 7966 insertions(+), 59 deletions(-)
> create mode 100644 Documentation/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.34.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-05-02 13:04   ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02 13:04 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

[...]

>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

Arkadiusz, could you please expose pin for netdev in ice as well?


>  mlx5: Implement SyncE support using DPLL infrastructure

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-02 13:04   ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02 13:04 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

[...]

>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

Arkadiusz, could you please expose pin for netdev in ice as well?


>  mlx5: Implement SyncE support using DPLL infrastructure

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-02 15:38     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02 15:38 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>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.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> MAINTAINERS                 |   8 +
> drivers/Kconfig             |   2 +
> drivers/Makefile            |   1 +
> drivers/dpll/Kconfig        |   7 +
> drivers/dpll/Makefile       |  10 +
> drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    | 113 +++++
> drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |  27 +
> include/linux/dpll.h        | 274 ++++++++++
> include/uapi/linux/dpll.h   |   2 +
> 11 files changed, 2355 insertions(+)
> 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 include/linux/dpll.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index ebd26b3ca90e..710976c0737e 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6302,6 +6302,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 <vadfed@fb.com>
>+L:	netdev@vger.kernel.org
>+S:	Maintained
>+F:	drivers/dpll/*
>+F:	include/net/dpll.h
>+F:	include/uapi/linux/dpll.h
>+
> DRBD DRIVER
> M:	Philipp Reisner <philipp.reisner@linbit.com>
> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>diff --git a/drivers/Kconfig b/drivers/Kconfig
>index 968bd0a6fd78..453df9e1210d 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
> 
> source "drivers/hte/Kconfig"
> 
>+source "drivers/dpll/Kconfig"
>+
> endmenu
>diff --git a/drivers/Makefile b/drivers/Makefile
>index 20b118dca999..9ffb554507ef 100644
>--- a/drivers/Makefile
>+++ b/drivers/Makefile
>@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
> obj-$(CONFIG_PECI)		+= peci/
> obj-$(CONFIG_HTE)		+= hte/
> obj-$(CONFIG_DRM_ACCEL)		+= accel/
>+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..803bb5db7793
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,10 @@
>+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..8a2370740026
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,939 @@
>+// 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_xa_lock);

Why this is called "xa_lock"? It protects much more than that. Call it
dpll_big_lock while you are at it.


>+
>+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
>+ *
>+ * 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_device_get_by_name - find dpll device by it's id

"by name" instead of "by id" ?


>+ * @bus_name: bus name of searched dpll
>+ * @dev_name: dev name of searched dpll
>+ *
>+ * Return:
>+ * * dpll_device struct if found
>+ * * NULL otherwise
>+ */
>+struct dpll_device *
>+dpll_device_get_by_name(const char *bus_name, const char *device_name)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long i;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+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.
>+ *
>+ * 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
>+ *
>+ * Decrement refcount of existing pin reference on given xarray.
>+ * If all registrations are lifted delete the reference and free its memory.
>+ *
>+ * 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.
>+ *
>+ * 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
>+ *
>+ * Decrement refcount of existing dpll reference on given xarray.
>+ * If all references are dropped, delete the reference and free its memory.
>+ */
>+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_dplls: dpll_pin_ref xarray holding dplls
>+ * @dpll: pointer to a dpll
>+ *
>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>+ *
>+ * 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;
>+}
>+
>+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.
>+ *
>+ * Return:
>+ * * dpll_device struct pointer if succeeded
>+ * * ERR_PTR(X) - failed allocation
>+ */
>+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.
>+ *
>+ * Return:
>+ * * valid dpll_device struct pointer if succeeded
>+ * * ERR_PTR of an error
>+ */
>+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_xa_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_xa_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
>+ *
>+ * 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_xa_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_xa_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
>+ * @owner: pointer to owner device
>+ *
>+ * Make dpll device available for user space.
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL on failure

From what I see, this function returns "-EEXIST" as well. Btw, what
benefit this "table" brings? Perhaps could be avoided in the whole code?


>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 const struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)
>+{
>+	struct dpll_device_registration *reg;
>+	bool first_registration = false;
>+
>+	if (WARN_ON(!ops || !owner))
>+		return -EINVAL;
>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll_xa_lock);
>+	reg = dpll_device_registration_find(dpll, ops, priv);
>+	if (reg) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return -EEXIST;
>+	}
>+
>+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+	if (!reg) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return -EEXIST;
>+	}
>+	reg->ops = ops;
>+	reg->priv = priv;
>+
>+	dpll->dev.bus = owner->bus;

This is definitelly odd. You basicall take PCI bus for example and
pretend some other device to be there. Why exactly this dev is needed at
all? I don't see the need, you only abuse it to store strings you
expose over Netlink.

Please remove dpll->dev entirely. Expose module_name, clock_id and
device_idx directly over Netlink as separate attributes.


>+	dpll->parent = owner;


You don't use dpll->parent. Please remove and remove also "owner" arg of
this function.



>+	dpll->type = type;
>+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
>+		     dpll->clock_id, dpll->device_idx);
>+
>+	first_registration = list_empty(&dpll->registration_list);
>+	list_add_tail(&reg->list, &dpll->registration_list);
>+	if (!first_registration) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return 0;
>+	}
>+
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_xa_lock);
>+	dpll_notify_device_create(dpll);
>+
>+	return 0;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_register);
>+
>+/**
>+ * dpll_device_unregister - deregister dpll device
>+ * @dpll: registered dpll pointer
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Deregister device, make it unavailable for userspace.
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll,
>+			    const struct dpll_device_ops *ops, void *priv)
>+{
>+	struct dpll_device_registration *reg;
>+
>+	mutex_lock(&dpll_xa_lock);
>+	ASSERT_DPLL_REGISTERED(dpll);
>+
>+	reg = dpll_device_registration_find(dpll, ops, priv);
>+	if (WARN_ON(!reg)) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return;
>+	}
>+	list_del(&reg->list);
>+	kfree(reg);
>+
>+	if (!list_empty(&dpll->registration_list)) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return;
>+	}
>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_xa_lock);
>+	dpll_notify_device_delete(dpll);
>+}
>+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
>+ *
>+ * Return:
>+ * valid allocated dpll_pin struct pointer if succeeded
>+ * ERR_PTR of an error
>+ */
>+static struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,

DPLL_A_PIN_IDX is u32, in struct dpll_pin it is u32.
Why here you have only u8? Please sync.


>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret, fs_size;
>+
>+	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;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->label)) {
>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>+	if (!pin->prop.label) {
>+		ret = -ENOMEM;
>+		goto err;
>+	}
>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.type = prop->type;
>+	pin->prop.capabilities = prop->capabilities;

Just assing the prop pointer to pin->prop and you are done, no. Why you
need to copy the internals? Driver should behave and pass static const
pointer here (it is common in cases like this).


>+	if (prop->freq_supported_num) {
>+		fs_size = sizeof(*pin->prop.freq_supported) *
>+			  prop->freq_supported_num;
>+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
>+		if (!pin->prop.freq_supported) {
>+			ret = -ENOMEM;
>+			goto err;
>+		}
>+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
>+		pin->prop.freq_supported_num = prop->freq_supported_num;
>+	}
>+	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->prop.label);
>+	kfree(pin->rclk_dev_name);
>+	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.
>+ *
>+ * 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
>+ * @dpll: dpll_device struct pointer
>+ *
>+ * Drop reference for a pin, if all references are gone, delete pin object.
>+ */
>+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->prop.label);
>+		kfree(pin->prop.freq_supported);
>+		kfree(pin->rclk_dev_name);
>+		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,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	if (WARN_ON(!ops))
>+		return -EINVAL;
>+
>+	if (rclk_device_name && !pin->rclk_dev_name) {
>+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
>+		if (!pin->rclk_dev_name)
>+			return -ENOMEM;
>+	}
>+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>+	if (ret)
>+		goto rclk_free;
>+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>+	if (ret)
>+		goto ref_pin_del;
>+	else
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
>+
>+	return ret;
>+
>+ref_pin_del:
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>+rclk_free:
>+	kfree(pin->rclk_dev_name);
>+	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
>+ * @rclk_device: pointer to recovered clock device
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL - missing dpll or pin

Incorrect.


>+ * * -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,
>+		  struct device *rclk_device)
>+{
>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>+	int ret;
>+
>+	mutex_lock(&dpll_xa_lock);

You have to make sure that dpll and pin are created with same module and
clock_id. Check and WARN_ON& bail out here.


>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>+	mutex_unlock(&dpll_xa_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 - deregister 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
>+ */
>+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_xa_lock);
>+	__dpll_pin_unregister(dpll, pin, ops, priv);
>+	mutex_unlock(&dpll_xa_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
>+ * @rclk_device: pointer to recovered clock device
>+ *
>+ * Register a pin with a parent pin, create references between them and
>+ * between newly registered pin and dplls connected with a parent pin.
>+ *
>+ * 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 device *rclk_device)
>+{
>+	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_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
>+			mutex_unlock(&dpll_xa_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 - deregister 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
>+ *
>+ * 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_xa_lock);
>+	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);
>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>+				       DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>+
>+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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_priv(const struct dpll_device *dpll)

I don't see where you call this with const *. Avoid const here which
will allow you to remove the cast below.


>+{
>+	struct dpll_device_registration *reg;
>+
>+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
>+	return reg->priv;
>+}
>+
>+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;
>+}
>+
>+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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_registration *reg;
>+	struct dpll_pin_ref *ref;
>+
>+	ref = xa_load((struct xarray *)&dpll->pin_refs, pin->pin_idx);

IDK, this sort of "unconst" cast always spells. Could you please
avoid them in the entire code?


>+	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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>+			   const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_registration *reg;
>+	struct dpll_pin_ref *ref;
>+
>+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
>+	if (!ref)
>+		return NULL;
>+	reg = dpll_pin_registration_first(ref);
>+	return reg->priv;
>+}
>+
>+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;
>+}
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	return 0;
>+
>+error:
>+	mutex_destroy(&dpll_xa_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..e905c1088568
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,113 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
>+ * @id:			unique id number for each device
>+ * @dev_driver_id:	id given by dev driver
>+ * @clock_id:		unique identifier (clock_id) of a dpll
>+ * @module:		module of creator
>+ * @dev:		struct device for this dpll device
>+ * @parent:		parent device
>+ * @ops:		operations this &dpll_device supports
>+ * @lock:		mutex to serialize operations
>+ * @type:		type of a dpll
>+ * @pins:		list of pointers to pins registered with this dpll
>+ * @mode_supported_mask: mask of supported modes
>+ * @refcount:		refcount
>+ * @priv:		pointer to private information of owner
>+ **/
>+struct dpll_device {
>+	u32 id;
>+	u32 device_idx;
>+	u64 clock_id;
>+	struct module *module;
>+	struct device dev;
>+	struct device *parent;
>+	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
>+ * @idx:		unique idx given by alloc on global pin's XA
>+ * @dev_driver_id:	id given by dev driver
>+ * @clock_id:		clock_id of creator
>+ * @module:		module of creator
>+ * @dpll_refs:		hold referencees to dplls that pin is registered with
>+ * @pin_refs:		hold references to pins that pin is registered with
>+ * @prop:		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;
>+	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(const struct dpll_device *dpll);
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin);
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>+			   const 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);
>+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
>+					    const char *dev_name);
>+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_xa_lock;
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..1eb0b4a2fce4
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,972 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Generic netlink for DPLL management framework
>+ *
>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates

It's 2023. You still live in the past :)



>+ *
>+ */
>+#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>
>+
>+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;
>+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))

In this version, only ID is a handle.


>+		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, const struct dpll_pin *pin,
>+		      struct dpll_pin_ref *ref,
>+		      struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const struct dpll_pin *pin,
>+			       struct dpll_pin_ref *ref,
>+			       struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const struct dpll_pin *pin,
>+			   struct dpll_pin_ref *ref,
>+			   struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const 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);
>+	const 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 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);
>+
>+		ppin = ref->pin;
>+
>+		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, &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_PARENT_IDX, ppin->pin_idx)) {
>+			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_DEVICE);
>+		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;
>+		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)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	if (pin->rclk_dev_name)
>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;
>+	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, 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;
>+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>+			  &dpll->clock_id, 0))
>+		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;
>+
>+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+					 dpll, dpll_priv(dpll), freq, extack);
>+		if (ret)
>+			return -EFAULT;
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+			  u32 parent_idx, enum dpll_pin_state state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops;
>+	struct dpll_pin_ref *pin_ref, *parent_ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
>+	       //	dpll_pin_get_by_idx(dpll, parent_idx);

Leftover?



>+	if (!parent_ref)
>+		return -EINVAL;
>+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>+	if (!pin_ref)
>+		return -EINVAL;
>+	ops = dpll_pin_ops(pin_ref);
>+	if (!ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ops->state_on_pin_set(pin_ref->pin,
>+				  dpll_pin_on_pin_priv(parent_ref->pin,
>+						       pin_ref->pin),
>+				  parent_ref->pin, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
>+			       DPLL_A_PIN_STATE);
>+
>+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops;
>+	struct dpll_pin_ref *ref;
>+	u32 prio = nla_get_u8(prio_attr);
>+
>+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
>+		       struct netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction = nla_get_u8(a);
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+
>+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+				       dpll, dpll_priv(dpll), direction,
>+				       extack))
>+			return -EFAULT;
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct genl_info *info)
>+{
>+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
>+	bool parent_present = false;
>+	int rem, ret = -EINVAL;
>+	struct nlattr *a;
>+	u32 parent_idx;
>+
>+	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_DIRECTION:
>+			ret = dpll_pin_direction_set(pin, a, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLL_A_PIN_PRIO:
>+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLL_A_PIN_PARENT_IDX:

See my comment for dpll_pin_pre_doit(), please change this to
PIN_PARENT_ID and use uniqueue xarray id handle for parent pin.


>+			parent_present = true;
>+			parent_idx = nla_get_u32(a);
>+			break;
>+		case DPLL_A_PIN_STATE:
>+			state = nla_get_u8(a);
>+			break;
>+		default:
>+			break;
>+		}


Why do you have to iterate here? Why simple
	if (attr_x)
		ret = dpll_pin_x_set()

is not enough?

Is it because of state? if yes:
	if (attr_state)
		if (attr_parent)
			dpll_pin_on_pin_state_set()
		else
			dpll_pin_state_set()




>+	}
>+	if (state != DPLL_PIN_STATE_UNSPEC) {
>+		if (!parent_present) {
>+			ret = dpll_pin_state_set(dpll, pin, state,
>+						 info->extack);
>+			if (ret)
>+				return ret;
>+		} else {
>+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
>+							state, info->extack);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct dpll_pin *pin = info->user_ptr[1];
>+
>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>+}
>+
>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_pin *pin = info->user_ptr[1];
>+	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;
>+}
>+
>+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 *attr;
>+	enum dpll_mode mode;
>+	int rem, ret = 0;
>+
>+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(attr)) {
>+		case DPLL_A_MODE:

Again, why loop? I don't see any sane reason, is there any?



>+			mode = nla_get_u8(attr);
>+
>+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
>+					    info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+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_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_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_xa_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_xa_lock);
>+	return -ENODEV;
>+}
>+
>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		    struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_xa_lock);
>+}
>+
>+int dpll_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		      struct genl_info *info)
>+{
>+	int ret = dpll_pre_doit(ops, skb, info);
>+	struct dpll_pin_ref *pin_ref;
>+	struct dpll_device *dpll;
>+
>+	if (ret)
>+		return ret;
>+	dpll = info->user_ptr[0];
>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>+		ret = -EINVAL;
>+		goto unlock_dev;
>+	}
>+	pin_ref = xa_load(&dpll->pin_refs,
>+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));

This is inconsistent, also incorrect.

You use DPLL_A_ID that is stored in dpll_device_xa as a handle for device.
That is fine if we consider Jakub's desire to have this randomly
generated id as a handle (I find is questinable, but can live with it).

But pins are independent on a single DPLL and could be attached to
multiple ones. Using a single DPLL_A_ID as handle here (dpll_pre_doit)
for all operations is plain wrong.

For example for frequency or direction set, you don't need it in code as
you iterate over all attacheds DPLL devices. Confusing to require DPLL
device handle for that operation when you change setup for all of them.
That is wrong.

Also, you have global dpll_pin_xa. Yet you don't expose this ID over
netlink. To be consistent with device handle, you should:
1) expose pin->id over DPLL_A_PIN_ID
2) use this DPLL_A_PIN_ID as a sole pin handle for dpll_pin_xa lookup.

For DPLL-PIN tuple operations (prio_set and state_on_dpll_set)
you should process the dpll device handle (DPLL_A_ID) where it is needed
In the similar way you process parent id now where is it needed
(state_on_pin_set)

For GET/DUMP command, this does not also make sense.
Check out __dpll_cmd_pin_dump_one()

You just use the "first dpll" for the handle. Just use the pin->id as I
suggested above.

Makes sense?

Please make sure you maintain the same handle attrs in the notification
messages as well.



>+	if (!pin_ref) {
>+		ret = -ENODEV;
>+		goto unlock_dev;
>+	}
>+	info->user_ptr[1] = pin_ref->pin;
>+
>+	return 0;
>+
>+unlock_dev:
>+	mutex_unlock(&dpll_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			struct genl_info *info)
>+{
>+	dpll_post_doit(ops, skb, info);
>+}
>+
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>+{
>+	return dpll_pre_dumpit(cb);
>+}
>+
>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>+{
>+	return dpll_post_dumpit(cb);
>+}
>+
>+static int
>+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct dpll_pin *parent,
>+			 enum dplla attr)
>+{
>+	int ret = dpll_msg_add_dev_handle(msg, dpll);
>+	struct dpll_pin_ref *ref = NULL;

Pointless init.


>+	enum dpll_pin_state state;
>+
>+	if (ret)
>+		return ret;
>+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>+		return -EMSGSIZE;
>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_LOCK_STATUS:
>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_TEMP:
>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_PIN_FREQUENCY:
>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);

Why exactly did you ignore my request I put in the previous version
review asking to maintain the same nesting scheme for GET cmd and
notification messages? Honestly, the silent ignores I'm getting
all along the review of this patchset is very frustrating. Please don't
do it. Either ack and change or provide exaplanation why your code is
fine.

So could you please fix this?
Again, please make sure that the notification messages have attributes
in exactly the same place as GET cmd (think of it as the rest of the
attrs in the message is filtered out). Makes possible to use the same
userspace parsing code both messages.



>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			const struct dpll_pin_ops *ops;
>+			void *priv = dpll_pin_on_pin_priv(parent, pin);
>+
>+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
>+			if (!ref)
>+				return -EFAULT;
>+			ops = dpll_pin_ops(ref);
>+			if (!ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ops->state_on_pin_get(pin, priv, parent,
>+						    &state, NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->pin_idx))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+			if (!ref)
>+				return -EFAULT;
>+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
>+							     NULL);
>+			if (ret)
>+				return ret;
>+		}
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	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_msg_add_dev_handle(msg, dpll);
>+	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;
>+}
>+
>+static int
>+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
>+		       struct dpll_pin *parent, enum dplla attr)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
>+			  DPLL_EVENT_DEVICE_CHANGE);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
>+	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_notify_device_create(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);

Quite odd. Consider rename of dpll_send_event_create()


>+}
>+
>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
>+{
>+	if (WARN_ON(!dpll))
>+		return -EINVAL;
>+
>+	return dpll_send_event_change(dpll, NULL, NULL, attr);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr)
>+{
>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>+
>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+			   struct dpll_pin *parent, enum dplla attr)
>+{
>+	return dpll_send_event_change(dpll, pin, parent, attr);
>+}
>+
>+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..952e0335595e
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,27 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+/**
>+ * dpll_notify_device_create - notify that the device has been created
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_create(struct dpll_device *dpll);
>+
>+
>+/**
>+ * dpll_notify_device_delete - notify that the device has been deleted
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_delete(struct dpll_device *dpll);
>+
>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+			   struct dpll_pin *parent, enum dplla attr);
>+
>+int __init dpll_netlink_init(void);
>+void dpll_netlink_finish(void);
>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>new file mode 100644
>index 000000000000..5194efaf55a8
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,274 @@
>+/* 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,
>+				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,
>+				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 *label;
>+	enum dpll_pin_type type;
>+	unsigned long capabilities;
>+	u32 freq_supported_num;
>+	struct dpll_pin_frequency *freq_supported;
>+};
>+
>+/**
>+ * dpll_device_get - find or create dpll_device object
>+ * @clock_id: a system unique number for a device
>+ * @dev_driver_id: index of dpll device on parent device
>+ * @module: register module
>+ *
>+ * Returns:
>+ * * pointer to initialized dpll - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_device
>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>+
>+/**
>+ * dpll_device_put - caller drops reference to the device, free resources
>+ * @dpll: dpll device pointer
>+ *
>+ * If all dpll_device_get callers drops their reference, the dpll device
>+ * resources are freed.
>+ */
>+void dpll_device_put(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_device_register - register device, make it visible in the subsystem.
>+ * @dpll: reference previously allocated with dpll_device_get
>+ * @type: type of dpll
>+ * @ops: callbacks
>+ * @priv: private data of registerer
>+ * @owner: device struct of the owner
>+ *
>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 const struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner);
>+
>+/**
>+ * dpll_device_unregister - deregister registered dpll
>+ * @dpll: pointer to dpll
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll,
>+			    const struct dpll_device_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_get - get reference or create new pin object
>+ * @clock_id: a system unique number of a device
>+ * @@dev_driver_id: index of dpll device on parent device
>+ * @module: register module
>+ * @pin_prop: constant properities of a pin
>+ *
>+ * find existing pin with given clock_id, @dev_driver_id and module, or create new
>+ * and returen its reference.
>+ *
>+ * Returns:
>+ * * pointer to initialized pin - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_pin
>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>+	      const struct dpll_pin_properties *prop);
>+
>+/**
>+ * dpll_pin_register - register pin with a dpll device
>+ * @dpll: pointer to dpll object to register pin with
>+ * @pin: pointer to allocated pin object being registered with dpll
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * Register previously allocated pin object with a dpll device.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this dpll,
>+ * * -EBUSY - couldn't allocate id for a pin.
>+ */

I don't follow. In one of the previous version reviews I asked if you
can have the function docs only in one place, preferably in .c. But you
have there here in .h as well. Why? They are inconsistent. Could you
please remove them from .h?



>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      const struct dpll_pin_ops *ops, void *priv,
>+		      struct device *rclk_device);

As I asked in the previous version, could you please remove this rclk_device
pointer. This is replaced by my patch allowing to expose dpll pin for
netdev over RTnetlink.



>+
>+/**
>+ * dpll_pin_unregister - deregister pin from a dpll device
>+ * @dpll: pointer to dpll object to deregister pin from
>+ * @pin: pointer to allocated pin object being deregistered from dpll
>+ * @ops: ops for a dpll pin ops
>+ * @priv: pointer to private information of owner
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ */
>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>+			 const struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
>+ * @pin: pointer to allocated pin
>+ *
>+ * Pins shall be deregistered from all dpll devices before putting them,
>+ * otherwise the memory won't be freed.
>+ */
>+void dpll_pin_put(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>+ * @parent: parent pin pointer
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ */
>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+			     const struct dpll_pin_ops *ops, void *priv,
>+			     struct device *rclk_device);
>+
>+/**
>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>+ * @parent: parent pin pointer
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ */
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>+				const struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_device_notify - notify on dpll device change
>+ * @dpll: dpll device pointer
>+ * @attr: changed attribute
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr);
>+
>+
>+
>+#endif
>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>index e188bc189754..75eeaa4396eb 100644
>--- a/include/uapi/linux/dpll.h
>+++ b/include/uapi/linux/dpll.h
>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> 
> #define DPLL_PIN_FREQUENCY_1_HZ		1
> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
> 
> /**
>  * enum dpll_pin_state - defines possible states of a pin, valid values for
>-- 
>2.34.1
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-02 15:38     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-02 15:38 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>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.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> MAINTAINERS                 |   8 +
> drivers/Kconfig             |   2 +
> drivers/Makefile            |   1 +
> drivers/dpll/Kconfig        |   7 +
> drivers/dpll/Makefile       |  10 +
> drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    | 113 +++++
> drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |  27 +
> include/linux/dpll.h        | 274 ++++++++++
> include/uapi/linux/dpll.h   |   2 +
> 11 files changed, 2355 insertions(+)
> 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 include/linux/dpll.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index ebd26b3ca90e..710976c0737e 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6302,6 +6302,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 <vadfed@fb.com>
>+L:	netdev@vger.kernel.org
>+S:	Maintained
>+F:	drivers/dpll/*
>+F:	include/net/dpll.h
>+F:	include/uapi/linux/dpll.h
>+
> DRBD DRIVER
> M:	Philipp Reisner <philipp.reisner@linbit.com>
> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>diff --git a/drivers/Kconfig b/drivers/Kconfig
>index 968bd0a6fd78..453df9e1210d 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
> 
> source "drivers/hte/Kconfig"
> 
>+source "drivers/dpll/Kconfig"
>+
> endmenu
>diff --git a/drivers/Makefile b/drivers/Makefile
>index 20b118dca999..9ffb554507ef 100644
>--- a/drivers/Makefile
>+++ b/drivers/Makefile
>@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
> obj-$(CONFIG_PECI)		+= peci/
> obj-$(CONFIG_HTE)		+= hte/
> obj-$(CONFIG_DRM_ACCEL)		+= accel/
>+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..803bb5db7793
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,10 @@
>+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..8a2370740026
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,939 @@
>+// 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_xa_lock);

Why this is called "xa_lock"? It protects much more than that. Call it
dpll_big_lock while you are at it.


>+
>+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
>+ *
>+ * 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_device_get_by_name - find dpll device by it's id

"by name" instead of "by id" ?


>+ * @bus_name: bus name of searched dpll
>+ * @dev_name: dev name of searched dpll
>+ *
>+ * Return:
>+ * * dpll_device struct if found
>+ * * NULL otherwise
>+ */
>+struct dpll_device *
>+dpll_device_get_by_name(const char *bus_name, const char *device_name)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long i;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+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.
>+ *
>+ * 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
>+ *
>+ * Decrement refcount of existing pin reference on given xarray.
>+ * If all registrations are lifted delete the reference and free its memory.
>+ *
>+ * 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.
>+ *
>+ * 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
>+ *
>+ * Decrement refcount of existing dpll reference on given xarray.
>+ * If all references are dropped, delete the reference and free its memory.
>+ */
>+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_dplls: dpll_pin_ref xarray holding dplls
>+ * @dpll: pointer to a dpll
>+ *
>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>+ *
>+ * 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;
>+}
>+
>+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.
>+ *
>+ * Return:
>+ * * dpll_device struct pointer if succeeded
>+ * * ERR_PTR(X) - failed allocation
>+ */
>+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.
>+ *
>+ * Return:
>+ * * valid dpll_device struct pointer if succeeded
>+ * * ERR_PTR of an error
>+ */
>+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_xa_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_xa_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
>+ *
>+ * 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_xa_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_xa_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
>+ * @owner: pointer to owner device
>+ *
>+ * Make dpll device available for user space.
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL on failure

From what I see, this function returns "-EEXIST" as well. Btw, what
benefit this "table" brings? Perhaps could be avoided in the whole code?


>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 const struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)
>+{
>+	struct dpll_device_registration *reg;
>+	bool first_registration = false;
>+
>+	if (WARN_ON(!ops || !owner))
>+		return -EINVAL;
>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll_xa_lock);
>+	reg = dpll_device_registration_find(dpll, ops, priv);
>+	if (reg) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return -EEXIST;
>+	}
>+
>+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+	if (!reg) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return -EEXIST;
>+	}
>+	reg->ops = ops;
>+	reg->priv = priv;
>+
>+	dpll->dev.bus = owner->bus;

This is definitelly odd. You basicall take PCI bus for example and
pretend some other device to be there. Why exactly this dev is needed at
all? I don't see the need, you only abuse it to store strings you
expose over Netlink.

Please remove dpll->dev entirely. Expose module_name, clock_id and
device_idx directly over Netlink as separate attributes.


>+	dpll->parent = owner;


You don't use dpll->parent. Please remove and remove also "owner" arg of
this function.



>+	dpll->type = type;
>+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
>+		     dpll->clock_id, dpll->device_idx);
>+
>+	first_registration = list_empty(&dpll->registration_list);
>+	list_add_tail(&reg->list, &dpll->registration_list);
>+	if (!first_registration) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return 0;
>+	}
>+
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_xa_lock);
>+	dpll_notify_device_create(dpll);
>+
>+	return 0;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_register);
>+
>+/**
>+ * dpll_device_unregister - deregister dpll device
>+ * @dpll: registered dpll pointer
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Deregister device, make it unavailable for userspace.
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll,
>+			    const struct dpll_device_ops *ops, void *priv)
>+{
>+	struct dpll_device_registration *reg;
>+
>+	mutex_lock(&dpll_xa_lock);
>+	ASSERT_DPLL_REGISTERED(dpll);
>+
>+	reg = dpll_device_registration_find(dpll, ops, priv);
>+	if (WARN_ON(!reg)) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return;
>+	}
>+	list_del(&reg->list);
>+	kfree(reg);
>+
>+	if (!list_empty(&dpll->registration_list)) {
>+		mutex_unlock(&dpll_xa_lock);
>+		return;
>+	}
>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_xa_lock);
>+	dpll_notify_device_delete(dpll);
>+}
>+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
>+ *
>+ * Return:
>+ * valid allocated dpll_pin struct pointer if succeeded
>+ * ERR_PTR of an error
>+ */
>+static struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,

DPLL_A_PIN_IDX is u32, in struct dpll_pin it is u32.
Why here you have only u8? Please sync.


>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret, fs_size;
>+
>+	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;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->label)) {
>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>+	if (!pin->prop.label) {
>+		ret = -ENOMEM;
>+		goto err;
>+	}
>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.type = prop->type;
>+	pin->prop.capabilities = prop->capabilities;

Just assing the prop pointer to pin->prop and you are done, no. Why you
need to copy the internals? Driver should behave and pass static const
pointer here (it is common in cases like this).


>+	if (prop->freq_supported_num) {
>+		fs_size = sizeof(*pin->prop.freq_supported) *
>+			  prop->freq_supported_num;
>+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
>+		if (!pin->prop.freq_supported) {
>+			ret = -ENOMEM;
>+			goto err;
>+		}
>+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
>+		pin->prop.freq_supported_num = prop->freq_supported_num;
>+	}
>+	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->prop.label);
>+	kfree(pin->rclk_dev_name);
>+	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.
>+ *
>+ * 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
>+ * @dpll: dpll_device struct pointer
>+ *
>+ * Drop reference for a pin, if all references are gone, delete pin object.
>+ */
>+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->prop.label);
>+		kfree(pin->prop.freq_supported);
>+		kfree(pin->rclk_dev_name);
>+		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,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	if (WARN_ON(!ops))
>+		return -EINVAL;
>+
>+	if (rclk_device_name && !pin->rclk_dev_name) {
>+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
>+		if (!pin->rclk_dev_name)
>+			return -ENOMEM;
>+	}
>+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>+	if (ret)
>+		goto rclk_free;
>+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>+	if (ret)
>+		goto ref_pin_del;
>+	else
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
>+
>+	return ret;
>+
>+ref_pin_del:
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>+rclk_free:
>+	kfree(pin->rclk_dev_name);
>+	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
>+ * @rclk_device: pointer to recovered clock device
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL - missing dpll or pin

Incorrect.


>+ * * -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,
>+		  struct device *rclk_device)
>+{
>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>+	int ret;
>+
>+	mutex_lock(&dpll_xa_lock);

You have to make sure that dpll and pin are created with same module and
clock_id. Check and WARN_ON& bail out here.


>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>+	mutex_unlock(&dpll_xa_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 - deregister 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
>+ */
>+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_xa_lock);
>+	__dpll_pin_unregister(dpll, pin, ops, priv);
>+	mutex_unlock(&dpll_xa_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
>+ * @rclk_device: pointer to recovered clock device
>+ *
>+ * Register a pin with a parent pin, create references between them and
>+ * between newly registered pin and dplls connected with a parent pin.
>+ *
>+ * 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 device *rclk_device)
>+{
>+	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_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
>+			mutex_unlock(&dpll_xa_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 - deregister 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
>+ *
>+ * 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_xa_lock);
>+	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);
>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>+				       DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>+
>+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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_priv(const struct dpll_device *dpll)

I don't see where you call this with const *. Avoid const here which
will allow you to remove the cast below.


>+{
>+	struct dpll_device_registration *reg;
>+
>+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
>+	return reg->priv;
>+}
>+
>+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;
>+}
>+
>+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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_registration *reg;
>+	struct dpll_pin_ref *ref;
>+
>+	ref = xa_load((struct xarray *)&dpll->pin_refs, pin->pin_idx);

IDK, this sort of "unconst" cast always spells. Could you please
avoid them in the entire code?


>+	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
>+ *
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>+			   const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_registration *reg;
>+	struct dpll_pin_ref *ref;
>+
>+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
>+	if (!ref)
>+		return NULL;
>+	reg = dpll_pin_registration_first(ref);
>+	return reg->priv;
>+}
>+
>+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;
>+}
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	return 0;
>+
>+error:
>+	mutex_destroy(&dpll_xa_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..e905c1088568
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,113 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
>+ * @id:			unique id number for each device
>+ * @dev_driver_id:	id given by dev driver
>+ * @clock_id:		unique identifier (clock_id) of a dpll
>+ * @module:		module of creator
>+ * @dev:		struct device for this dpll device
>+ * @parent:		parent device
>+ * @ops:		operations this &dpll_device supports
>+ * @lock:		mutex to serialize operations
>+ * @type:		type of a dpll
>+ * @pins:		list of pointers to pins registered with this dpll
>+ * @mode_supported_mask: mask of supported modes
>+ * @refcount:		refcount
>+ * @priv:		pointer to private information of owner
>+ **/
>+struct dpll_device {
>+	u32 id;
>+	u32 device_idx;
>+	u64 clock_id;
>+	struct module *module;
>+	struct device dev;
>+	struct device *parent;
>+	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
>+ * @idx:		unique idx given by alloc on global pin's XA
>+ * @dev_driver_id:	id given by dev driver
>+ * @clock_id:		clock_id of creator
>+ * @module:		module of creator
>+ * @dpll_refs:		hold referencees to dplls that pin is registered with
>+ * @pin_refs:		hold references to pins that pin is registered with
>+ * @prop:		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;
>+	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(const struct dpll_device *dpll);
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin);
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>+			   const 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);
>+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
>+					    const char *dev_name);
>+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_xa_lock;
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..1eb0b4a2fce4
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,972 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Generic netlink for DPLL management framework
>+ *
>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates

It's 2023. You still live in the past :)



>+ *
>+ */
>+#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>
>+
>+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;
>+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))

In this version, only ID is a handle.


>+		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, const struct dpll_pin *pin,
>+		      struct dpll_pin_ref *ref,
>+		      struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const struct dpll_pin *pin,
>+			       struct dpll_pin_ref *ref,
>+			       struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const struct dpll_pin *pin,
>+			   struct dpll_pin_ref *ref,
>+			   struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+	const 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, const 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);
>+	const 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 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);
>+
>+		ppin = ref->pin;
>+
>+		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, &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_PARENT_IDX, ppin->pin_idx)) {
>+			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_DEVICE);
>+		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;
>+		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)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	if (pin->rclk_dev_name)
>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;
>+	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, 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;
>+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>+			  &dpll->clock_id, 0))
>+		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;
>+
>+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+					 dpll, dpll_priv(dpll), freq, extack);
>+		if (ret)
>+			return -EFAULT;
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+			  u32 parent_idx, enum dpll_pin_state state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops;
>+	struct dpll_pin_ref *pin_ref, *parent_ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
>+	       //	dpll_pin_get_by_idx(dpll, parent_idx);

Leftover?



>+	if (!parent_ref)
>+		return -EINVAL;
>+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>+	if (!pin_ref)
>+		return -EINVAL;
>+	ops = dpll_pin_ops(pin_ref);
>+	if (!ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ops->state_on_pin_set(pin_ref->pin,
>+				  dpll_pin_on_pin_priv(parent_ref->pin,
>+						       pin_ref->pin),
>+				  parent_ref->pin, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
>+			       DPLL_A_PIN_STATE);
>+
>+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
>+{
>+	const struct dpll_pin_ops *ops;
>+	struct dpll_pin_ref *ref;
>+	u32 prio = nla_get_u8(prio_attr);
>+
>+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
>+		       struct netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction = nla_get_u8(a);
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+
>+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+				       dpll, dpll_priv(dpll), direction,
>+				       extack))
>+			return -EFAULT;
>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct genl_info *info)
>+{
>+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
>+	bool parent_present = false;
>+	int rem, ret = -EINVAL;
>+	struct nlattr *a;
>+	u32 parent_idx;
>+
>+	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_DIRECTION:
>+			ret = dpll_pin_direction_set(pin, a, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLL_A_PIN_PRIO:
>+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLL_A_PIN_PARENT_IDX:

See my comment for dpll_pin_pre_doit(), please change this to
PIN_PARENT_ID and use uniqueue xarray id handle for parent pin.


>+			parent_present = true;
>+			parent_idx = nla_get_u32(a);
>+			break;
>+		case DPLL_A_PIN_STATE:
>+			state = nla_get_u8(a);
>+			break;
>+		default:
>+			break;
>+		}


Why do you have to iterate here? Why simple
	if (attr_x)
		ret = dpll_pin_x_set()

is not enough?

Is it because of state? if yes:
	if (attr_state)
		if (attr_parent)
			dpll_pin_on_pin_state_set()
		else
			dpll_pin_state_set()




>+	}
>+	if (state != DPLL_PIN_STATE_UNSPEC) {
>+		if (!parent_present) {
>+			ret = dpll_pin_state_set(dpll, pin, state,
>+						 info->extack);
>+			if (ret)
>+				return ret;
>+		} else {
>+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
>+							state, info->extack);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct dpll_pin *pin = info->user_ptr[1];
>+
>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>+}
>+
>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_pin *pin = info->user_ptr[1];
>+	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;
>+}
>+
>+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 *attr;
>+	enum dpll_mode mode;
>+	int rem, ret = 0;
>+
>+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(attr)) {
>+		case DPLL_A_MODE:

Again, why loop? I don't see any sane reason, is there any?



>+			mode = nla_get_u8(attr);
>+
>+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
>+					    info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+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_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_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_xa_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_xa_lock);
>+	return -ENODEV;
>+}
>+
>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		    struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_xa_lock);
>+}
>+
>+int dpll_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		      struct genl_info *info)
>+{
>+	int ret = dpll_pre_doit(ops, skb, info);
>+	struct dpll_pin_ref *pin_ref;
>+	struct dpll_device *dpll;
>+
>+	if (ret)
>+		return ret;
>+	dpll = info->user_ptr[0];
>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>+		ret = -EINVAL;
>+		goto unlock_dev;
>+	}
>+	pin_ref = xa_load(&dpll->pin_refs,
>+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));

This is inconsistent, also incorrect.

You use DPLL_A_ID that is stored in dpll_device_xa as a handle for device.
That is fine if we consider Jakub's desire to have this randomly
generated id as a handle (I find is questinable, but can live with it).

But pins are independent on a single DPLL and could be attached to
multiple ones. Using a single DPLL_A_ID as handle here (dpll_pre_doit)
for all operations is plain wrong.

For example for frequency or direction set, you don't need it in code as
you iterate over all attacheds DPLL devices. Confusing to require DPLL
device handle for that operation when you change setup for all of them.
That is wrong.

Also, you have global dpll_pin_xa. Yet you don't expose this ID over
netlink. To be consistent with device handle, you should:
1) expose pin->id over DPLL_A_PIN_ID
2) use this DPLL_A_PIN_ID as a sole pin handle for dpll_pin_xa lookup.

For DPLL-PIN tuple operations (prio_set and state_on_dpll_set)
you should process the dpll device handle (DPLL_A_ID) where it is needed
In the similar way you process parent id now where is it needed
(state_on_pin_set)

For GET/DUMP command, this does not also make sense.
Check out __dpll_cmd_pin_dump_one()

You just use the "first dpll" for the handle. Just use the pin->id as I
suggested above.

Makes sense?

Please make sure you maintain the same handle attrs in the notification
messages as well.



>+	if (!pin_ref) {
>+		ret = -ENODEV;
>+		goto unlock_dev;
>+	}
>+	info->user_ptr[1] = pin_ref->pin;
>+
>+	return 0;
>+
>+unlock_dev:
>+	mutex_unlock(&dpll_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			struct genl_info *info)
>+{
>+	dpll_post_doit(ops, skb, info);
>+}
>+
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>+{
>+	return dpll_pre_dumpit(cb);
>+}
>+
>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>+{
>+	return dpll_post_dumpit(cb);
>+}
>+
>+static int
>+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct dpll_pin *parent,
>+			 enum dplla attr)
>+{
>+	int ret = dpll_msg_add_dev_handle(msg, dpll);
>+	struct dpll_pin_ref *ref = NULL;

Pointless init.


>+	enum dpll_pin_state state;
>+
>+	if (ret)
>+		return ret;
>+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>+		return -EMSGSIZE;
>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_LOCK_STATUS:
>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_TEMP:
>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_PIN_FREQUENCY:
>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);

Why exactly did you ignore my request I put in the previous version
review asking to maintain the same nesting scheme for GET cmd and
notification messages? Honestly, the silent ignores I'm getting
all along the review of this patchset is very frustrating. Please don't
do it. Either ack and change or provide exaplanation why your code is
fine.

So could you please fix this?
Again, please make sure that the notification messages have attributes
in exactly the same place as GET cmd (think of it as the rest of the
attrs in the message is filtered out). Makes possible to use the same
userspace parsing code both messages.



>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			const struct dpll_pin_ops *ops;
>+			void *priv = dpll_pin_on_pin_priv(parent, pin);
>+
>+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
>+			if (!ref)
>+				return -EFAULT;
>+			ops = dpll_pin_ops(ref);
>+			if (!ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ops->state_on_pin_get(pin, priv, parent,
>+						    &state, NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->pin_idx))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+			if (!ref)
>+				return -EFAULT;
>+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
>+							     NULL);
>+			if (ret)
>+				return ret;
>+		}
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	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_msg_add_dev_handle(msg, dpll);
>+	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;
>+}
>+
>+static int
>+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
>+		       struct dpll_pin *parent, enum dplla attr)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
>+			  DPLL_EVENT_DEVICE_CHANGE);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
>+	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_notify_device_create(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);

Quite odd. Consider rename of dpll_send_event_create()


>+}
>+
>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
>+{
>+	if (WARN_ON(!dpll))
>+		return -EINVAL;
>+
>+	return dpll_send_event_change(dpll, NULL, NULL, attr);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr)
>+{
>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>+
>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+			   struct dpll_pin *parent, enum dplla attr)
>+{
>+	return dpll_send_event_change(dpll, pin, parent, attr);
>+}
>+
>+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..952e0335595e
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,27 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+/**
>+ * dpll_notify_device_create - notify that the device has been created
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_create(struct dpll_device *dpll);
>+
>+
>+/**
>+ * dpll_notify_device_delete - notify that the device has been deleted
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_delete(struct dpll_device *dpll);
>+
>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+			   struct dpll_pin *parent, enum dplla attr);
>+
>+int __init dpll_netlink_init(void);
>+void dpll_netlink_finish(void);
>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>new file mode 100644
>index 000000000000..5194efaf55a8
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,274 @@
>+/* 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,
>+				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,
>+				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 *label;
>+	enum dpll_pin_type type;
>+	unsigned long capabilities;
>+	u32 freq_supported_num;
>+	struct dpll_pin_frequency *freq_supported;
>+};
>+
>+/**
>+ * dpll_device_get - find or create dpll_device object
>+ * @clock_id: a system unique number for a device
>+ * @dev_driver_id: index of dpll device on parent device
>+ * @module: register module
>+ *
>+ * Returns:
>+ * * pointer to initialized dpll - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_device
>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>+
>+/**
>+ * dpll_device_put - caller drops reference to the device, free resources
>+ * @dpll: dpll device pointer
>+ *
>+ * If all dpll_device_get callers drops their reference, the dpll device
>+ * resources are freed.
>+ */
>+void dpll_device_put(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_device_register - register device, make it visible in the subsystem.
>+ * @dpll: reference previously allocated with dpll_device_get
>+ * @type: type of dpll
>+ * @ops: callbacks
>+ * @priv: private data of registerer
>+ * @owner: device struct of the owner
>+ *
>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 const struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner);
>+
>+/**
>+ * dpll_device_unregister - deregister registered dpll
>+ * @dpll: pointer to dpll
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll,
>+			    const struct dpll_device_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_get - get reference or create new pin object
>+ * @clock_id: a system unique number of a device
>+ * @@dev_driver_id: index of dpll device on parent device
>+ * @module: register module
>+ * @pin_prop: constant properities of a pin
>+ *
>+ * find existing pin with given clock_id, @dev_driver_id and module, or create new
>+ * and returen its reference.
>+ *
>+ * Returns:
>+ * * pointer to initialized pin - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_pin
>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>+	      const struct dpll_pin_properties *prop);
>+
>+/**
>+ * dpll_pin_register - register pin with a dpll device
>+ * @dpll: pointer to dpll object to register pin with
>+ * @pin: pointer to allocated pin object being registered with dpll
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * Register previously allocated pin object with a dpll device.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this dpll,
>+ * * -EBUSY - couldn't allocate id for a pin.
>+ */

I don't follow. In one of the previous version reviews I asked if you
can have the function docs only in one place, preferably in .c. But you
have there here in .h as well. Why? They are inconsistent. Could you
please remove them from .h?



>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      const struct dpll_pin_ops *ops, void *priv,
>+		      struct device *rclk_device);

As I asked in the previous version, could you please remove this rclk_device
pointer. This is replaced by my patch allowing to expose dpll pin for
netdev over RTnetlink.



>+
>+/**
>+ * dpll_pin_unregister - deregister pin from a dpll device
>+ * @dpll: pointer to dpll object to deregister pin from
>+ * @pin: pointer to allocated pin object being deregistered from dpll
>+ * @ops: ops for a dpll pin ops
>+ * @priv: pointer to private information of owner
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ */
>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>+			 const struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
>+ * @pin: pointer to allocated pin
>+ *
>+ * Pins shall be deregistered from all dpll devices before putting them,
>+ * otherwise the memory won't be freed.
>+ */
>+void dpll_pin_put(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>+ * @parent: parent pin pointer
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ */
>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+			     const struct dpll_pin_ops *ops, void *priv,
>+			     struct device *rclk_device);
>+
>+/**
>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>+ * @parent: parent pin pointer
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ * @rclk_device: pointer to device struct if pin is used for recovery of a clock
>+ * from that device
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ */
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>+				const struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_device_notify - notify on dpll device change
>+ * @dpll: dpll device pointer
>+ * @attr: changed attribute
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr);
>+
>+
>+
>+#endif
>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>index e188bc189754..75eeaa4396eb 100644
>--- a/include/uapi/linux/dpll.h
>+++ b/include/uapi/linux/dpll.h
>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> 
> #define DPLL_PIN_FREQUENCY_1_HZ		1
> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
> 
> /**
>  * enum dpll_pin_state - defines possible states of a pin, valid values for
>-- 
>2.34.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-03  8:09     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-03  8:09 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

[...]


>+static struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret, fs_size;
>+
>+	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;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->label)) {

Why exactly label has to be mandatory? In mlx5, I have no use for it.
Please make it optional. IIRC, I asked for this in the last review
as well.


>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);

Labels should be static const string. Do you see a usecase when you need
to dup it? If not, remove this please.



>+	if (!pin->prop.label) {
>+		ret = -ENOMEM;
>+		goto err;
>+	}


[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-03  8:09     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-03  8:09 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

[...]


>+static struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret, fs_size;
>+
>+	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;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->label)) {

Why exactly label has to be mandatory? In mlx5, I have no use for it.
Please make it optional. IIRC, I asked for this in the last review
as well.


>+		ret = -EINVAL;
>+		goto err;
>+	}
>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);

Labels should be static const string. Do you see a usecase when you need
to dup it? If not, remove this please.



>+	if (!pin->prop.label) {
>+		ret = -ENOMEM;
>+		goto err;
>+	}


[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-03 12:18     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-03 12:18 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>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 <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>---
> 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   | 1929 +++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
> drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
> 8 files changed, 2125 insertions(+), 89 deletions(-)
> 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 5d89392f969b..6c198cd92d49 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -74,6 +74,7 @@
> #include "ice_lag.h"
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -201,6 +202,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,
>@@ -512,6 +514,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 */
> };
> 
>@@ -635,6 +638,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..3217fb36dd12
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,1929 @@
>+// 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>
>+#include <uapi/linux/dpll.h>

Don't include uapi directly. I'm pretty sure I had the same comment the
last time as well.


>+
>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
>+#define ICE_DPLL_LOCK_TRIES		1000
>+#define ICE_DPLL_PIN_IDX_INVALID	0xff
>+
>+/**
>+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[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_SOURCE,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>+};
>+
>+/**
>+ * pin_type_name - string names of ice pin types
>+ */
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>+};
>+
>+/**
>+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin index of a searched dpll subsystem
>+ * pin pointer.
>+ *
>+ * Return:
>+ * * valid index for a given pin & pin type found on pf internal dpll struct
>+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
>+ */
>+static u32
>+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
>+		 enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)

How this can happen? If not, remove.


>+		return ICE_DPLL_PIN_IDX_INVALID;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else {
>+		return ICE_DPLL_PIN_IDX_INVALID;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return i;
>+
>+	return ICE_DPLL_PIN_IDX_INVALID;
>+}
>+
>+/**
>+ * 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)

On many places you call this without saving the return value to int
variable. You should do that and propagate the error value.


>+{
>+	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_find_pin - find ice_dpll_pin on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin info pointer holding data of given dpll
>+ * subsystem pin pointer.
>+ *
>+ * Return:
>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
>+ * found in pf internal pin data.
>+ * * NULL - if pin was not found.
>+ */
>+static struct ice_dpll_pin
>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>+	      enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)
>+		return NULL;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		if (pin == pf->dplls.rclk.pin)
>+			return &pf->dplls.rclk;
>+	} else {
>+		return NULL;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return &pins[i];
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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,
>+		      const enum ice_dpll_pin_type pin_type, const u32 freq)
>+{
>+	u8 flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		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);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+	} else {

How exactly this can happen? If not, avoid it.
And use switch-case for enum values


>+		ret = -EINVAL;
>+	}
>+
>+	if (ret) {
>+		dev_dbg(ice_pf_to_dev(pf),

dev_err


>+			"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);
>+	} else {
>+		pin->freq = freq;
>+	}
>+
>+	return ret;

Usual pattern is:
	ret = something() //switch-case in this case
	if (ret)
		return ret;
	return 0;
Easier to follow.


>+}
>+
>+/**
>+ * 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
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * 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,
>+		       const u32 frequency,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);

This does not make any sense to me. You should avoid the lookups and
remove ice_find_pin() function entirely. The purpose of having pin_priv
is to carry the struct ice_dpll_pin * directly. You should pass it down
during pin register.

pf pointer is stored in dpll_priv.


>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");

That would be very odd message the user would see :)


>+		goto unlock;
>+	}
>+
>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);

Why you need to print "ret"? It is propagated to the caller as a return
value.


>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_frequency_set - source 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_source_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, (u32)frequency, extack,

Avoid the cast here, not needed.

The dpll core should do check if user passes frequency which is
supported, so you don't care about the overflow either.


>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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, 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.
>+ *
>+ * 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, u64 *frequency,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	*frequency = (u64)(p->freq);

Drop the pointless cast.


>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_frequency_get - source 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 source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_source_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, frequency, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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, 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.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags[0];
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	}

switch-case


>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),

dev_err?


>+			"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);
>+	else
>+		pin->flags[0] = flags;
>+
>+	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.
>+ *
>+ * 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)
>+{
>+	u8 flags = pin->flags[0];
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	}

switch-case?


>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),

dev_err?


>+			"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);
>+	else
>+		pin->flags[0] = flags;
>+
>+	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 source pin states are separated for each
>+ * dpll, for rclk pins states are separated for each parent.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			  const enum ice_dpll_pin_type pin_type)
>+{
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		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])) {

Don't do "!!", it's not needed. You have this on multiple places. Please
reduce.


>+			if (pin->pin) {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					pin->pin == pf->dplls.eec.active_source ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					pin->pin == pf->dplls.pps.active_source ?
>+					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;
>+		}
>+	} else if (pin_type == 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;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		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;
>+		}
>+	}

Perhaps:

	switch (pin_type) {
	case ICE_DPLL_PIN_TYPE_SOURCE:
		..
	case ICE_DPLL_PIN_TYPE_OUTPUT:
		..

?

>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_find_dpll - find ice_dpll on a pf
>+ * @pf: private board structure
>+ * @dpll: kernel's dpll_device pointer to be searched
>+ *
>+ * 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_source_prio_set - set source 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_source_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_dbg(ice_pf_to_dev(pf),

dev_err


>+			"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.
>+ *
>+ * 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_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return -EINVAL;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	d = ice_find_dpll(pf, dpll);

Another example of odd and unneeded lookup. Register dpll device with
struct ice_dpll *d as a priv. Store pf pointer there in struct ice_dpll.
And remove ice_find_dpll() entirely.



>+	if (!d)
>+		return -EFAULT;
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>+	*status = ice_dpll_status[d->dpll_state];
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * 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)
>+{
>+	struct ice_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return -EINVAL;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	d = ice_find_dpll(pf, dpll);
>+	ice_dpll_cb_unlock(pf);
>+	if (!d)
>+		return -EFAULT;
>+	*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,
>+				    const enum dpll_mode mode,
>+				    struct netlink_ext_ack *extack)
>+{
>+	struct ice_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return false;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return false;
>+	d = ice_find_dpll(pf, dpll);
>+	ice_dpll_cb_unlock(pf);
>+	if (!d)
>+		return false;
>+	if (mode == DPLL_MODE_AUTOMATIC)
>+		return true;
>+
>+	return false;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_set - set pin's state on dpll
>+ * @dpll: dpll being configured
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ * @pin_type: type of a pin
>+ *
>+ * Set pin state on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin, void *pin_priv,
>+		       const enum dpll_pin_state state,

Why you use const with enums?


>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		if (state == DPLL_PIN_STATE_SELECTABLE)
>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		if (state == DPLL_PIN_STATE_CONNECTED)
>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);

switch-case?

Perhaps it would be nicer to do this in ice_dpll_output_state_set()
and ice_dpll_source_state_set() directly?


>+	}
>+	if (!ret)
>+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+	dev_dbg(ice_pf_to_dev(pf),

dev_err in case ret != 0 ?


>+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
>+		__func__, dpll, pin, p, pf, state, 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.
>+ *
>+ * 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,
>+				     const enum dpll_pin_state state,
>+				     struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_source_state_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)
>+{
>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_pin_state_get - set pin's state on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_pin_state_get(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin, void *pin_priv,
>+		       enum dpll_pin_state *state,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	struct ice_dpll *d;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	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_SOURCE)
>+		*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);
>+	dev_dbg(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.
>+ *
>+ * 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(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_state_get - get source 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 source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_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_pf *pf = pin_priv;
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		NL_SET_ERR_MSG(extack, "dpll not found");
>+		goto unlock;
>+	}
>+	*prio = d->input_prio[p->idx];
>+	ret = 0;
>+unlock:
>+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_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_pf *pf = pin_priv;
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX) {
>+		NL_SET_ERR_MSG(extack, "prio out of range");
>+		return ret;
>+	}
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		NL_SET_ERR_MSG(extack, "dpll not found");
>+		goto unlock;
>+	}
>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);

Why you need to print "ret"? It is propagated to the caller as a return
value.


>+unlock:
>+	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_source_direction - callback for get source 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 source pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int ice_dpll_source_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_SOURCE;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_source_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
>+ *
>+ * 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,
>+					  const enum dpll_pin_state state,
>+					  struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);

Again, this does not make sense. You need struct ice_dpll_pin * related
to parent. That should be parent priv and passed to dpll subsystem
during registration and put as an "void * parent_pin_priv" arg
to .state_on_pin_set() op. Whenever you do lookup like this, it is
most usually wrong.



>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)

Can't you just store idx in struct ice_dpll_pin to avoid lookups like
this one?


>+			hw_idx = i;
>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+
>+	if ((enable && !!(p->flags[hw_idx] &
>+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
>+	    (!enable && !(p->flags[hw_idx] &
>+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
>+		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 ret:%d\n",
>+		__func__, parent_pin, pin, pf, 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure

I wonder how valuable this return values table is for a reader.
Not much I suppose. Do you need it in comments to all the functions you
have here?


>+ */
>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>+					  void *pin_priv,
>+					  const struct dpll_pin *parent_pin,
>+					  enum dpll_pin_state *state,
>+					  struct netlink_ext_ack *extack)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;

Reverse christmas tree ordering please.


>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	if (!pf)

How exacly this can happen. My wild guess is it can't. Don't do such
pointless checks please, confuses the reader.


>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>+			hw_idx = i;
>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+
>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (ret)
>+		goto unlock;
>+
>+	if (!!(p->flags[hw_idx] &
>+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))

Avoid needless "!!".


>+		*state = DPLL_PIN_STATE_CONNECTED;
>+	else
>+		*state = DPLL_PIN_STATE_DISCONNECTED;

Use ternary operator perhaps?


>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, parent_pin, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+static struct dpll_pin_ops ice_dpll_rclk_ops = {

const.


>+	.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_source_direction,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_source_ops = {

const.


>+	.frequency_get = ice_dpll_source_frequency_get,
>+	.frequency_set = ice_dpll_source_frequency_set,
>+	.state_on_dpll_get = ice_dpll_source_state_get,
>+	.state_on_dpll_set = ice_dpll_source_state_set,
>+	.prio_get = ice_dpll_source_prio_get,
>+	.prio_set = ice_dpll_source_prio_set,
>+	.direction_get = ice_dpll_source_direction,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_output_ops = {

const.

>+	.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 struct dpll_device_ops ice_dpll_ops = {

const.


>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+};
>+
>+/**
>+ * ice_dpll_release_info - release memory allocated for pins
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ */
>+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
>+ * @pf: board private structure
>+ *
>+ * Deregister from parent pins and release resources in dpll subsystem.
>+ */
>+static void
>+ice_dpll_release_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>+	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;
>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>+					   &ice_dpll_rclk_ops, pf);
>+	}
>+	dpll_pin_put(rclk->pin);
>+	rclk->pin = NULL;
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
>+ * @pf: board private structure
>+ * @dpll_eec: dpll_eec dpll pointer
>+ * @dpll_pps: dpll_pps dpll pointer
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Deregister and free pins of a given array of pins from dpll devices
>+ * registered in dpll subsystem.
>+ */
>+static void
>+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>+		      int count, struct dpll_pin_ops *ops, bool cgu)
>+{
>+	int i;
>+
>+	for (i = 0; i < count; i++) {
>+		struct ice_dpll_pin *p = &pins[i];
>+
>+		if (p && !IS_ERR_OR_NULL(p->pin)) {
>+			if (cgu && dpll_eec)
>+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
>+			if (cgu && dpll_pps)
>+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
>+			dpll_pin_put(p->pin);
>+			p->pin = NULL;
>+		}
>+	}
>+}
>+
>+/**
>+ * ice_dpll_register_pins - register pins with a dpll
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	u32 rclk_idx;
>+	int ret, i;
>+
>+	ops = &ice_dpll_source_ops;
>+	pins = pf->dplls.inputs;
>+	for (i = 0; i < pf->dplls.num_inputs; i++) {
>+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
>+					   THIS_MODULE, &pins[i].prop);
>+		if (IS_ERR_OR_NULL(pins[i].pin)) {

How exactly dpll_pin_get() can return NULL? It can't, use IS_ERR.
Same in ice_dpll_release_pins() and two occurances below.



>+			pins[i].pin = NULL;
>+			return -ENOMEM;
>+		}
>+		if (cgu) {
>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>+						pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;
>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>+						pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;

You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
here.


>+		}
>+	}
>+	if (cgu) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>+						   i + pf->dplls.num_inputs,
>+						   THIS_MODULE, &pins[i].prop);
>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>+				pins[i].pin = NULL;
>+				return -ENOMEM;

Don't make up error values when you get them from the function you call:
	return PTR_ERR(pins[i].pin);

>+			}
>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;
>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;

You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
here.


>+                                              ops, pf, NULL);  


>+		}
>+	}
>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
>+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
>+					  THIS_MODULE, &pf->dplls.rclk.prop);
>+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
>+		pf->dplls.rclk.pin = NULL;
>+		return -ENOMEM;

Don't make up error values when you get them from the function you call:
	return PTR_ERR(pf->dplls.rclk.pin);


>+	}
>+	ops = &ice_dpll_rclk_ops;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>+		struct dpll_pin *parent =
>+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>+
>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>+					       ops, pf, dev);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ * @clock_id: holds generated clock_id
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ */
>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>+{
>+	*clock_id = pci_get_dsn(pf->pdev);
>+}

How about:

static u64 ice_generate_clock_id(struct ice_pf *pf)
{
	return pci_get_dsn(pf->pdev);
}

??


>+
>+/**
>+ * ice_dpll_init_dplls
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Get dplls instances for this board, if cgu is controlled by this NIC,
>+ * register dpll with callbacks ops
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation fails
>+ */
>+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	int ret = -ENOMEM;
>+	u64 clock_id;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf->dplls.eec.dpll_idx,
>+					     THIS_MODULE);
>+	if (!pf->dplls.eec.dpll) {

You have to use IS_ERR()


>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
>+		return ret;
>+	}
>+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf->dplls.pps.dpll_idx,
>+					     THIS_MODULE);
>+	if (!pf->dplls.pps.dpll) {

You have to use IS_ERR()

	
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
>+		goto put_eec;
>+	}
>+
>+	if (cgu) {
>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>+					   &ice_dpll_ops, pf, dev);
>+		if (ret)
>+			goto put_pps;
>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>+					   &ice_dpll_ops, pf, dev);
>+		if (ret)

You are missing call to dpll_device_unregister(pf->dplls.eec.dpll, DPLL_TYPE_EEC
here. Fix the error path.


>+			goto put_pps;
>+	}
>+
>+	return 0;
>+
>+put_pps:
>+	dpll_device_put(pf->dplls.pps.dpll);
>+	pf->dplls.pps.dpll = NULL;
>+put_eec:
>+	dpll_device_put(pf->dplls.eec.dpll);
>+	pf->dplls.eec.dpll = NULL;
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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.
>+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
>+		p = &pf->dplls.inputs[d->source_idx];
>+		return ice_dpll_pin_state_update(pf, p,
>+						 ICE_DPLL_PIN_TYPE_SOURCE);
>+	}
>+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
>+		d->active_source = NULL;
>+		p = &pf->dplls.inputs[d->source_idx];
>+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+	} else if (d->source_idx != d->prev_source_idx) {
>+		p = &pf->dplls.inputs[d->prev_source_idx];
>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+		p = &pf->dplls.inputs[d->source_idx];
>+		d->active_source = p->pin;
>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
>+	}
>+	if (d->prev_source != d->active_source) {
>+		d->prev_source = d->active_source;
>+		if (d->active_source)
>+			dpll_pin_notify(d->dpll, d->active_source,
>+					DPLL_A_PIN_STATE);

Didn't the state of the previously active source change as well? You
need to send notification for that too.


>+	}
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ */
>+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.
>+ *
>+ * 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_release_all - disable support for DPLL and unregister dpll device
>+ * @pf: board private structure
>+ * @cgu: if cgu is controlled by this driver instance
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	ice_dpll_release_rclk_pin(pf);
>+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
>+			      d->num_inputs, &ice_dpll_source_ops, cgu);
>+	mutex_unlock(&pf->dplls.lock);
>+	if (cgu) {
>+		mutex_lock(&pf->dplls.lock);

Interesting, you lock again a lock you just unlocked. One might wonder
why you just don't move the call to mutex_unlock below this "if
section".


>+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
>+				      d->num_outputs,
>+				      &ice_dpll_output_ops, cgu);
>+		mutex_unlock(&pf->dplls.lock);
>+	}
>+	ice_dpll_release_info(pf);
>+	if (dp->dpll) {
>+		mutex_lock(&pf->dplls.lock);
>+		if (cgu)
>+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
>+		dpll_device_put(dp->dpll);
>+		mutex_unlock(&pf->dplls.lock);
>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>+	}
>+
>+	if (de->dpll) {
>+		mutex_lock(&pf->dplls.lock);
>+		if (cgu)
>+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
>+		dpll_device_put(de->dpll);
>+		mutex_unlock(&pf->dplls.lock);
>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>+	}
>+
>+	if (cgu) {
>+		mutex_lock(&pf->dplls.lock);
>+		kthread_cancel_delayed_work_sync(&d->work);
>+		if (d->kworker) {
>+			kthread_destroy_worker(d->kworker);
>+			d->kworker = NULL;
>+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>+		}
>+		mutex_unlock(&pf->dplls.lock);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * Handles the cleanup work required after dpll initialization,
>+ * freeing resources and unregistering the dpll.
>+ */
>+void ice_dpll_release(struct ice_pf *pf)
>+{
>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		ice_dpll_release_all(pf,
>+				     ice_is_feature_supported(pf, ICE_F_CGU));
>+		mutex_destroy(&pf->dplls.lock);
>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init_direct_pins - initializes source or output pins information
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information about input or output pins, cache them in pins struct.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int
>+ice_dpll_init_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;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+		input = true;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+		input = false;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].prop.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;

It is a flag. Common is to use bit op &= instead.


>+		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;
>+	}
>+
>+	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
>+ */
>+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.label = dev_name(dev);
>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+
>+	return ice_dpll_pin_state_update(pf, pin,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_init_pins - init pins wrapper
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Wraps functions for pin inti.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
>+		return ice_dpll_init_direct_pins(pf, pin_type);
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>+		return ice_dpll_init_direct_pins(pf, pin_type);
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
>+		return ice_dpll_init_rclk_pin(pf);
>+	else
>+		return -EINVAL;

How this can happen?

How about:
	switch (pin_type) {
	case ICE_DPLL_PIN_TYPE_SOURCE:
	case ICE_DPLL_PIN_TYPE_OUTPUT:
		return ice_dpll_init_direct_pins(pf, pin_type);
	case ICE_DPLL_PIN_TYPE_RCLK_SOURCE:
		return ice_dpll_init_rclk_pin(pf);
	}
?



>+}
>+
>+/**
>+ * 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 - error
>+ */
>+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;
>+	u8 base_rclk_idx;
>+
>+	ice_generate_clock_id(pf, &d->clock_id);
>+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (ret)
>+		goto release_info;
>+
>+	if (cgu) {
>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+		if (!d->outputs)
>+			goto release_info;
>+
>+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+		if (ret)
>+			goto release_info;
>+	}
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	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;
>+
>+release_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_release_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_init - initialize dplls support
>+ * @pf: board private structure
>+ *
>+ * Set up the device dplls registering 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
>+ */
>+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 release;
>+	err = ice_dpll_init_dplls(pf, cgu_present);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, cgu_present);

This should be rather called "ice_dpll_init_pins()" to be in sync with
ice_dpll_init_dplls() as it is doing more then just registration.


>+	if (err)
>+		goto release;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	if (cgu_present) {
>+		err = ice_dpll_init_worker(pf);
>+		if (err)
>+			goto release;
>+	}
>+	mutex_unlock(&d->lock);
>+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>+
>+	return err;
>+
>+release:
>+	ice_dpll_release_all(pf, cgu_present);
>+	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\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..aad48b9910b7
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,101 @@
>+/* 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
>+ * @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;
>+	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
>+ * @dpll_idx: index of dpll on the NIC
>+ * @source_idx: source currently selected
>+ * @prev_source_idx: source previously selected
>+ * @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_source: pointer to active source pin
>+ * @prev_source: pointer to previous active source pin
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	int dpll_idx;
>+	u8 source_idx;
>+	u8 prev_source_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_source;
>+	struct dpll_pin *prev_source;
>+};
>+
>+/** 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
>+ * @num_rclk: number of recovered clock pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ */
>+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_release(struct ice_pf *pf);
>+
>+int ice_dpll_rclk_init(struct ice_pf *pf);
>+
>+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4821,6 +4821,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");
>@@ -4847,6 +4851,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_release(pf);
> }
> 
> static void ice_init_wakeup(struct ice_pf *pf)
>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>index e9a371fa038b..39b692945f73 100644
>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
> }
> 
> /**
>- * ice_cgu_get_pin_sig_type_mask
>+ * 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
>  *
>- * Return: signal type bit mask of a pin.
>+ * Get frequency supported number and array of supported frequencies.
>+ *
>+ * Return: array of supported frequencies for given pin.
>  */
>-unsigned long
>-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
> 	int t_size;
> 
>+	*num = 0;
> 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
>-
> 	if (!t)
>-		return 0;
>-
>+		return NULL;
> 	if (pin >= t_size)
>-		return 0;
>+		return NULL;
>+	*num = t[pin].freq_supp_num;
> 
>-	return t[pin].sig_type_mask;
>+	return t[pin].freq_supp;
> }
> 
> /**
>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>index d09e5bca0ff1..4568b0403cd7 100644
>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
> 	NUM_SI_CGU_OUTPUT_PINS
> };
> 
>-#define MAX_CGU_PIN_NAME_LEN		16
>-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
>-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
>+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[MAX_CGU_PIN_NAME_LEN];
>+	char *name;
> 	u8 index;
> 	enum dpll_pin_type type;
>-	unsigned long sig_type_mask;
>+	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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
>-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+	{ "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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
> 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
>-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
> };
> 
>@@ -429,8 +419,8 @@ 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);
>-unsigned long
>-ice_cgu_get_pin_freq_mask(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,
>-- 
>2.34.1
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-03 12:18     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-03 12:18 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>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 <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>---
> 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   | 1929 +++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
> drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
> 8 files changed, 2125 insertions(+), 89 deletions(-)
> 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 5d89392f969b..6c198cd92d49 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -74,6 +74,7 @@
> #include "ice_lag.h"
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -201,6 +202,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,
>@@ -512,6 +514,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 */
> };
> 
>@@ -635,6 +638,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..3217fb36dd12
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,1929 @@
>+// 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>
>+#include <uapi/linux/dpll.h>

Don't include uapi directly. I'm pretty sure I had the same comment the
last time as well.


>+
>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
>+#define ICE_DPLL_LOCK_TRIES		1000
>+#define ICE_DPLL_PIN_IDX_INVALID	0xff
>+
>+/**
>+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[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_SOURCE,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>+};
>+
>+/**
>+ * pin_type_name - string names of ice pin types
>+ */
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>+};
>+
>+/**
>+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin index of a searched dpll subsystem
>+ * pin pointer.
>+ *
>+ * Return:
>+ * * valid index for a given pin & pin type found on pf internal dpll struct
>+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
>+ */
>+static u32
>+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
>+		 enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)

How this can happen? If not, remove.


>+		return ICE_DPLL_PIN_IDX_INVALID;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else {
>+		return ICE_DPLL_PIN_IDX_INVALID;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return i;
>+
>+	return ICE_DPLL_PIN_IDX_INVALID;
>+}
>+
>+/**
>+ * 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)

On many places you call this without saving the return value to int
variable. You should do that and propagate the error value.


>+{
>+	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_find_pin - find ice_dpll_pin on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin info pointer holding data of given dpll
>+ * subsystem pin pointer.
>+ *
>+ * Return:
>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
>+ * found in pf internal pin data.
>+ * * NULL - if pin was not found.
>+ */
>+static struct ice_dpll_pin
>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>+	      enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)
>+		return NULL;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		if (pin == pf->dplls.rclk.pin)
>+			return &pf->dplls.rclk;
>+	} else {
>+		return NULL;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return &pins[i];
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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,
>+		      const enum ice_dpll_pin_type pin_type, const u32 freq)
>+{
>+	u8 flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		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);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+	} else {

How exactly this can happen? If not, avoid it.
And use switch-case for enum values


>+		ret = -EINVAL;
>+	}
>+
>+	if (ret) {
>+		dev_dbg(ice_pf_to_dev(pf),

dev_err


>+			"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);
>+	} else {
>+		pin->freq = freq;
>+	}
>+
>+	return ret;

Usual pattern is:
	ret = something() //switch-case in this case
	if (ret)
		return ret;
	return 0;
Easier to follow.


>+}
>+
>+/**
>+ * 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
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * 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,
>+		       const u32 frequency,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);

This does not make any sense to me. You should avoid the lookups and
remove ice_find_pin() function entirely. The purpose of having pin_priv
is to carry the struct ice_dpll_pin * directly. You should pass it down
during pin register.

pf pointer is stored in dpll_priv.


>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");

That would be very odd message the user would see :)


>+		goto unlock;
>+	}
>+
>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);

Why you need to print "ret"? It is propagated to the caller as a return
value.


>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_frequency_set - source 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_source_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, (u32)frequency, extack,

Avoid the cast here, not needed.

The dpll core should do check if user passes frequency which is
supported, so you don't care about the overflow either.


>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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, 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.
>+ *
>+ * 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, u64 *frequency,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	*frequency = (u64)(p->freq);

Drop the pointless cast.


>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_frequency_get - source 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 source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_source_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, frequency, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * 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.
>+ *
>+ * 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, 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.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags[0];
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	}

switch-case


>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),

dev_err?


>+			"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);
>+	else
>+		pin->flags[0] = flags;
>+
>+	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.
>+ *
>+ * 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)
>+{
>+	u8 flags = pin->flags[0];
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	}

switch-case?


>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),

dev_err?


>+			"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);
>+	else
>+		pin->flags[0] = flags;
>+
>+	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 source pin states are separated for each
>+ * dpll, for rclk pins states are separated for each parent.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			  const enum ice_dpll_pin_type pin_type)
>+{
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		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])) {

Don't do "!!", it's not needed. You have this on multiple places. Please
reduce.


>+			if (pin->pin) {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					pin->pin == pf->dplls.eec.active_source ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					pin->pin == pf->dplls.pps.active_source ?
>+					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;
>+		}
>+	} else if (pin_type == 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;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		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;
>+		}
>+	}

Perhaps:

	switch (pin_type) {
	case ICE_DPLL_PIN_TYPE_SOURCE:
		..
	case ICE_DPLL_PIN_TYPE_OUTPUT:
		..

?

>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_find_dpll - find ice_dpll on a pf
>+ * @pf: private board structure
>+ * @dpll: kernel's dpll_device pointer to be searched
>+ *
>+ * 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_source_prio_set - set source 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_source_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_dbg(ice_pf_to_dev(pf),

dev_err


>+			"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.
>+ *
>+ * 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_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return -EINVAL;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	d = ice_find_dpll(pf, dpll);

Another example of odd and unneeded lookup. Register dpll device with
struct ice_dpll *d as a priv. Store pf pointer there in struct ice_dpll.
And remove ice_find_dpll() entirely.



>+	if (!d)
>+		return -EFAULT;
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>+	*status = ice_dpll_status[d->dpll_state];
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * 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)
>+{
>+	struct ice_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return -EINVAL;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	d = ice_find_dpll(pf, dpll);
>+	ice_dpll_cb_unlock(pf);
>+	if (!d)
>+		return -EFAULT;
>+	*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,
>+				    const enum dpll_mode mode,
>+				    struct netlink_ext_ack *extack)
>+{
>+	struct ice_pf *pf = priv;
>+	struct ice_dpll *d;
>+
>+	if (!pf)
>+		return false;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return false;
>+	d = ice_find_dpll(pf, dpll);
>+	ice_dpll_cb_unlock(pf);
>+	if (!d)
>+		return false;
>+	if (mode == DPLL_MODE_AUTOMATIC)
>+		return true;
>+
>+	return false;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_set - set pin's state on dpll
>+ * @dpll: dpll being configured
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ * @pin_type: type of a pin
>+ *
>+ * Set pin state on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin, void *pin_priv,
>+		       const enum dpll_pin_state state,

Why you use const with enums?


>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		if (state == DPLL_PIN_STATE_SELECTABLE)
>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		if (state == DPLL_PIN_STATE_CONNECTED)
>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);

switch-case?

Perhaps it would be nicer to do this in ice_dpll_output_state_set()
and ice_dpll_source_state_set() directly?


>+	}
>+	if (!ret)
>+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+	dev_dbg(ice_pf_to_dev(pf),

dev_err in case ret != 0 ?


>+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
>+		__func__, dpll, pin, p, pf, state, 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.
>+ *
>+ * 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,
>+				     const enum dpll_pin_state state,
>+				     struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_source_state_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)
>+{
>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_pin_state_get - set pin's state on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_pin_state_get(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin, void *pin_priv,
>+		       enum dpll_pin_state *state,
>+		       struct netlink_ext_ack *extack,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	struct ice_dpll *d;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	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_SOURCE)
>+		*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);
>+	dev_dbg(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.
>+ *
>+ * 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(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_state_get - get source 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 source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_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_pf *pf = pin_priv;
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		NL_SET_ERR_MSG(extack, "dpll not found");
>+		goto unlock;
>+	}
>+	*prio = d->input_prio[p->idx];
>+	ret = 0;
>+unlock:
>+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_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_pf *pf = pin_priv;
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX) {
>+		NL_SET_ERR_MSG(extack, "prio out of range");
>+		return ret;
>+	}
>+
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p) {
>+		NL_SET_ERR_MSG(extack, "pin not found");
>+		goto unlock;
>+	}
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		NL_SET_ERR_MSG(extack, "dpll not found");
>+		goto unlock;
>+	}
>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);

Why you need to print "ret"? It is propagated to the caller as a return
value.


>+unlock:
>+	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_source_direction - callback for get source 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 source pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int ice_dpll_source_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_SOURCE;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_source_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
>+ *
>+ * 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,
>+					  const enum dpll_pin_state state,
>+					  struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>+	struct ice_pf *pf = pin_priv;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (!pf)
>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);

Again, this does not make sense. You need struct ice_dpll_pin * related
to parent. That should be parent priv and passed to dpll subsystem
during registration and put as an "void * parent_pin_priv" arg
to .state_on_pin_set() op. Whenever you do lookup like this, it is
most usually wrong.



>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)

Can't you just store idx in struct ice_dpll_pin to avoid lookups like
this one?


>+			hw_idx = i;
>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+
>+	if ((enable && !!(p->flags[hw_idx] &
>+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
>+	    (!enable && !(p->flags[hw_idx] &
>+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
>+		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 ret:%d\n",
>+		__func__, parent_pin, pin, pf, 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.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure

I wonder how valuable this return values table is for a reader.
Not much I suppose. Do you need it in comments to all the functions you
have here?


>+ */
>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>+					  void *pin_priv,
>+					  const struct dpll_pin *parent_pin,
>+					  enum dpll_pin_state *state,
>+					  struct netlink_ext_ack *extack)
>+{
>+	struct ice_pf *pf = pin_priv;
>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;

Reverse christmas tree ordering please.


>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	if (!pf)

How exacly this can happen. My wild guess is it can't. Don't do such
pointless checks please, confuses the reader.


>+		return ret;
>+	if (ice_dpll_cb_lock(pf))
>+		return -EBUSY;
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>+			hw_idx = i;
>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>+		goto unlock;
>+
>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (ret)
>+		goto unlock;
>+
>+	if (!!(p->flags[hw_idx] &
>+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))

Avoid needless "!!".


>+		*state = DPLL_PIN_STATE_CONNECTED;
>+	else
>+		*state = DPLL_PIN_STATE_DISCONNECTED;

Use ternary operator perhaps?


>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, parent_pin, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+static struct dpll_pin_ops ice_dpll_rclk_ops = {

const.


>+	.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_source_direction,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_source_ops = {

const.


>+	.frequency_get = ice_dpll_source_frequency_get,
>+	.frequency_set = ice_dpll_source_frequency_set,
>+	.state_on_dpll_get = ice_dpll_source_state_get,
>+	.state_on_dpll_set = ice_dpll_source_state_set,
>+	.prio_get = ice_dpll_source_prio_get,
>+	.prio_set = ice_dpll_source_prio_set,
>+	.direction_get = ice_dpll_source_direction,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_output_ops = {

const.

>+	.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 struct dpll_device_ops ice_dpll_ops = {

const.


>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+};
>+
>+/**
>+ * ice_dpll_release_info - release memory allocated for pins
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ */
>+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
>+ * @pf: board private structure
>+ *
>+ * Deregister from parent pins and release resources in dpll subsystem.
>+ */
>+static void
>+ice_dpll_release_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>+	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;
>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>+					   &ice_dpll_rclk_ops, pf);
>+	}
>+	dpll_pin_put(rclk->pin);
>+	rclk->pin = NULL;
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
>+ * @pf: board private structure
>+ * @dpll_eec: dpll_eec dpll pointer
>+ * @dpll_pps: dpll_pps dpll pointer
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Deregister and free pins of a given array of pins from dpll devices
>+ * registered in dpll subsystem.
>+ */
>+static void
>+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>+		      int count, struct dpll_pin_ops *ops, bool cgu)
>+{
>+	int i;
>+
>+	for (i = 0; i < count; i++) {
>+		struct ice_dpll_pin *p = &pins[i];
>+
>+		if (p && !IS_ERR_OR_NULL(p->pin)) {
>+			if (cgu && dpll_eec)
>+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
>+			if (cgu && dpll_pps)
>+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
>+			dpll_pin_put(p->pin);
>+			p->pin = NULL;
>+		}
>+	}
>+}
>+
>+/**
>+ * ice_dpll_register_pins - register pins with a dpll
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	u32 rclk_idx;
>+	int ret, i;
>+
>+	ops = &ice_dpll_source_ops;
>+	pins = pf->dplls.inputs;
>+	for (i = 0; i < pf->dplls.num_inputs; i++) {
>+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
>+					   THIS_MODULE, &pins[i].prop);
>+		if (IS_ERR_OR_NULL(pins[i].pin)) {

How exactly dpll_pin_get() can return NULL? It can't, use IS_ERR.
Same in ice_dpll_release_pins() and two occurances below.



>+			pins[i].pin = NULL;
>+			return -ENOMEM;
>+		}
>+		if (cgu) {
>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>+						pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;
>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>+						pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;

You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
here.


>+		}
>+	}
>+	if (cgu) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>+						   i + pf->dplls.num_inputs,
>+						   THIS_MODULE, &pins[i].prop);
>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>+				pins[i].pin = NULL;
>+				return -ENOMEM;

Don't make up error values when you get them from the function you call:
	return PTR_ERR(pins[i].pin);

>+			}
>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;
>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>+						ops, pf, NULL);
>+			if (ret)
>+				return ret;

You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
here.


>+                                              ops, pf, NULL);  


>+		}
>+	}
>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
>+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
>+					  THIS_MODULE, &pf->dplls.rclk.prop);
>+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
>+		pf->dplls.rclk.pin = NULL;
>+		return -ENOMEM;

Don't make up error values when you get them from the function you call:
	return PTR_ERR(pf->dplls.rclk.pin);


>+	}
>+	ops = &ice_dpll_rclk_ops;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>+		struct dpll_pin *parent =
>+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>+
>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>+					       ops, pf, dev);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ * @clock_id: holds generated clock_id
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ */
>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>+{
>+	*clock_id = pci_get_dsn(pf->pdev);
>+}

How about:

static u64 ice_generate_clock_id(struct ice_pf *pf)
{
	return pci_get_dsn(pf->pdev);
}

??


>+
>+/**
>+ * ice_dpll_init_dplls
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Get dplls instances for this board, if cgu is controlled by this NIC,
>+ * register dpll with callbacks ops
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation fails
>+ */
>+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	int ret = -ENOMEM;
>+	u64 clock_id;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf->dplls.eec.dpll_idx,
>+					     THIS_MODULE);
>+	if (!pf->dplls.eec.dpll) {

You have to use IS_ERR()


>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
>+		return ret;
>+	}
>+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf->dplls.pps.dpll_idx,
>+					     THIS_MODULE);
>+	if (!pf->dplls.pps.dpll) {

You have to use IS_ERR()

	
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
>+		goto put_eec;
>+	}
>+
>+	if (cgu) {
>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>+					   &ice_dpll_ops, pf, dev);
>+		if (ret)
>+			goto put_pps;
>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>+					   &ice_dpll_ops, pf, dev);
>+		if (ret)

You are missing call to dpll_device_unregister(pf->dplls.eec.dpll, DPLL_TYPE_EEC
here. Fix the error path.


>+			goto put_pps;
>+	}
>+
>+	return 0;
>+
>+put_pps:
>+	dpll_device_put(pf->dplls.pps.dpll);
>+	pf->dplls.pps.dpll = NULL;
>+put_eec:
>+	dpll_device_put(pf->dplls.eec.dpll);
>+	pf->dplls.eec.dpll = NULL;
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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.
>+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
>+		p = &pf->dplls.inputs[d->source_idx];
>+		return ice_dpll_pin_state_update(pf, p,
>+						 ICE_DPLL_PIN_TYPE_SOURCE);
>+	}
>+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
>+		d->active_source = NULL;
>+		p = &pf->dplls.inputs[d->source_idx];
>+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+	} else if (d->source_idx != d->prev_source_idx) {
>+		p = &pf->dplls.inputs[d->prev_source_idx];
>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+		p = &pf->dplls.inputs[d->source_idx];
>+		d->active_source = p->pin;
>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
>+	}
>+	if (d->prev_source != d->active_source) {
>+		d->prev_source = d->active_source;
>+		if (d->active_source)
>+			dpll_pin_notify(d->dpll, d->active_source,
>+					DPLL_A_PIN_STATE);

Didn't the state of the previously active source change as well? You
need to send notification for that too.


>+	}
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ */
>+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.
>+ *
>+ * 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_release_all - disable support for DPLL and unregister dpll device
>+ * @pf: board private structure
>+ * @cgu: if cgu is controlled by this driver instance
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	ice_dpll_release_rclk_pin(pf);
>+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
>+			      d->num_inputs, &ice_dpll_source_ops, cgu);
>+	mutex_unlock(&pf->dplls.lock);
>+	if (cgu) {
>+		mutex_lock(&pf->dplls.lock);

Interesting, you lock again a lock you just unlocked. One might wonder
why you just don't move the call to mutex_unlock below this "if
section".


>+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
>+				      d->num_outputs,
>+				      &ice_dpll_output_ops, cgu);
>+		mutex_unlock(&pf->dplls.lock);
>+	}
>+	ice_dpll_release_info(pf);
>+	if (dp->dpll) {
>+		mutex_lock(&pf->dplls.lock);
>+		if (cgu)
>+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
>+		dpll_device_put(dp->dpll);
>+		mutex_unlock(&pf->dplls.lock);
>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>+	}
>+
>+	if (de->dpll) {
>+		mutex_lock(&pf->dplls.lock);
>+		if (cgu)
>+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
>+		dpll_device_put(de->dpll);
>+		mutex_unlock(&pf->dplls.lock);
>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>+	}
>+
>+	if (cgu) {
>+		mutex_lock(&pf->dplls.lock);
>+		kthread_cancel_delayed_work_sync(&d->work);
>+		if (d->kworker) {
>+			kthread_destroy_worker(d->kworker);
>+			d->kworker = NULL;
>+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>+		}
>+		mutex_unlock(&pf->dplls.lock);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * Handles the cleanup work required after dpll initialization,
>+ * freeing resources and unregistering the dpll.
>+ */
>+void ice_dpll_release(struct ice_pf *pf)
>+{
>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		ice_dpll_release_all(pf,
>+				     ice_is_feature_supported(pf, ICE_F_CGU));
>+		mutex_destroy(&pf->dplls.lock);
>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init_direct_pins - initializes source or output pins information
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information about input or output pins, cache them in pins struct.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int
>+ice_dpll_init_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;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+		input = true;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+		input = false;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].prop.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;

It is a flag. Common is to use bit op &= instead.


>+		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;
>+	}
>+
>+	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
>+ */
>+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.label = dev_name(dev);
>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+
>+	return ice_dpll_pin_state_update(pf, pin,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_init_pins - init pins wrapper
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Wraps functions for pin inti.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
>+		return ice_dpll_init_direct_pins(pf, pin_type);
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>+		return ice_dpll_init_direct_pins(pf, pin_type);
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
>+		return ice_dpll_init_rclk_pin(pf);
>+	else
>+		return -EINVAL;

How this can happen?

How about:
	switch (pin_type) {
	case ICE_DPLL_PIN_TYPE_SOURCE:
	case ICE_DPLL_PIN_TYPE_OUTPUT:
		return ice_dpll_init_direct_pins(pf, pin_type);
	case ICE_DPLL_PIN_TYPE_RCLK_SOURCE:
		return ice_dpll_init_rclk_pin(pf);
	}
?



>+}
>+
>+/**
>+ * 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 - error
>+ */
>+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;
>+	u8 base_rclk_idx;
>+
>+	ice_generate_clock_id(pf, &d->clock_id);
>+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (ret)
>+		goto release_info;
>+
>+	if (cgu) {
>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+		if (!d->outputs)
>+			goto release_info;
>+
>+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+		if (ret)
>+			goto release_info;
>+	}
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	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;
>+
>+release_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_release_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_init - initialize dplls support
>+ * @pf: board private structure
>+ *
>+ * Set up the device dplls registering 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
>+ */
>+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 release;
>+	err = ice_dpll_init_dplls(pf, cgu_present);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, cgu_present);

This should be rather called "ice_dpll_init_pins()" to be in sync with
ice_dpll_init_dplls() as it is doing more then just registration.


>+	if (err)
>+		goto release;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	if (cgu_present) {
>+		err = ice_dpll_init_worker(pf);
>+		if (err)
>+			goto release;
>+	}
>+	mutex_unlock(&d->lock);
>+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>+
>+	return err;
>+
>+release:
>+	ice_dpll_release_all(pf, cgu_present);
>+	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\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..aad48b9910b7
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,101 @@
>+/* 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
>+ * @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;
>+	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
>+ * @dpll_idx: index of dpll on the NIC
>+ * @source_idx: source currently selected
>+ * @prev_source_idx: source previously selected
>+ * @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_source: pointer to active source pin
>+ * @prev_source: pointer to previous active source pin
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	int dpll_idx;
>+	u8 source_idx;
>+	u8 prev_source_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_source;
>+	struct dpll_pin *prev_source;
>+};
>+
>+/** 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
>+ * @num_rclk: number of recovered clock pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ */
>+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_release(struct ice_pf *pf);
>+
>+int ice_dpll_rclk_init(struct ice_pf *pf);
>+
>+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4821,6 +4821,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");
>@@ -4847,6 +4851,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_release(pf);
> }
> 
> static void ice_init_wakeup(struct ice_pf *pf)
>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>index e9a371fa038b..39b692945f73 100644
>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
> }
> 
> /**
>- * ice_cgu_get_pin_sig_type_mask
>+ * 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
>  *
>- * Return: signal type bit mask of a pin.
>+ * Get frequency supported number and array of supported frequencies.
>+ *
>+ * Return: array of supported frequencies for given pin.
>  */
>-unsigned long
>-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
> 	int t_size;
> 
>+	*num = 0;
> 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
>-
> 	if (!t)
>-		return 0;
>-
>+		return NULL;
> 	if (pin >= t_size)
>-		return 0;
>+		return NULL;
>+	*num = t[pin].freq_supp_num;
> 
>-	return t[pin].sig_type_mask;
>+	return t[pin].freq_supp;
> }
> 
> /**
>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>index d09e5bca0ff1..4568b0403cd7 100644
>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
> 	NUM_SI_CGU_OUTPUT_PINS
> };
> 
>-#define MAX_CGU_PIN_NAME_LEN		16
>-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
>-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
>+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[MAX_CGU_PIN_NAME_LEN];
>+	char *name;
> 	u8 index;
> 	enum dpll_pin_type type;
>-	unsigned long sig_type_mask;
>+	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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
>-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+	{ "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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
> 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
> 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
>-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>+		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,
>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
> };
> 
>@@ -429,8 +419,8 @@ 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);
>-unsigned long
>-ice_cgu_get_pin_freq_mask(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,
>-- 
>2.34.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04  9:27     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04  9:27 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>Implement basic DPLL operations in ptp_ocp driver as the
>simplest example of using new subsystem.
>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> drivers/ptp/Kconfig   |   1 +
> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
> 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>@@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
> 	SMA_MODE_OUT,
> };
> 
>+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {

const

>+	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 {
>@@ -295,6 +305,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;
>@@ -351,8 +362,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)
>@@ -836,6 +848,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[] = {
>@@ -856,31 +869,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 },
>@@ -891,15 +904,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 },
> 	{ }
> };
> 
>@@ -2283,22 +2296,34 @@ 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 = {

Why don't you have this as static const outside the function?


>+		.label = NULL,

Pointless 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.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;
> 	}
>@@ -2308,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);
>@@ -2330,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;
>@@ -2397,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);
> }
>@@ -2446,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 = {
>+		.label = NULL,
>+		.type = DPLL_PIN_TYPE_EXT,
>+		.capabilities = 0,

Same comment as to the similar prop struct above. Plus another pointless
init here.


>+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+		.freq_supported = ptp_ocp_sma_freq,
>+
>+	};
> 	u32 reg;
> 	int i;
> 
>@@ -2460,16 +2493,16 @@ 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.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) {
>@@ -2480,9 +2513,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;
> 		}
> 	}
>@@ -2549,6 +2586,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);
>@@ -2690,16 +2730,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;
>@@ -2734,6 +2767,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)
>@@ -4172,12 +4219,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;

Does your device support event delivery in case of the status change?
ice and mlx5 drivers do poll for changes in this area anyway. It's a
part of this patchset. You should do the same if your device does
not support events.

Could you please implement notifications using
dpll_device_notify() for status change and dpll_pin_notify() for pin
state change?



>+
>+	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_SOURCE :
>+				  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)

I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
is not set and therefore the check in dpll_pin_direction_set() will be
true and -EOPNOTSUPP will be returned from there.
Remove this.


>+		return -EOPNOTSUPP;
>+	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>+			    SMA_MODE_IN : SMA_MODE_OUT;
>+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);

You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
to accept struct ptp_ocp_sma_connector * instead avoiding the need for
tne sma_nr completely?


>+}
>+
>+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)

In that case, just fill up a single frequency in the properties,
avoid this check-fail and let the dpll core handle it.


>+		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);

1) void "()"s here.
2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
   this easier to follow? IDK, just a suggestion, take or leave.

Same applies to the the rest of similar occurances above.


>+	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,

This op is a leftover, in dpll core it is not called. This was removed
and agreed that drivers should implement state_on_dpll_get() op for pins
to see which one is connected.

Please fix here and remove the leftover from DPLL patch #2 as well.


>+	.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) {
>@@ -4227,8 +4410,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);

I suggested this the last time, but again: Could you please:
1) rename dpll_device_get to __dpll_device_get
2) introduce dpll_device_get as a macro filling up THIS_MODULE

Then drivers will just call always:
bp->dpll = dpll_device_get(clkid, 0);
and the macro will fillup the module automatically.

Please do the same for dpll_pin_get()


>+	if (IS_ERR(bp->dpll)) {
>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");

You need to fix your error path to call devlink_unregister() in this
case.


>+		goto out;
>+	}
>+
>+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>+	if (err)

You need to fix your error path to call dpll_device_put() in this
case.


>+		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], NULL);
>+		if (err) {
>+			dpll_pin_put(bp->sma[i].dpll_pin);
>+			goto out_dpll;
>+		}
>+	}
>+
>+	return 0;
>+out_dpll:
>+	while (i) {
>+		--i;

	while (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:
>@@ -4243,7 +4457,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) {

Remove this pointless check. It is always true.


>+			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.34.1
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
@ 2023-05-04  9:27     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04  9:27 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>Implement basic DPLL operations in ptp_ocp driver as the
>simplest example of using new subsystem.
>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> drivers/ptp/Kconfig   |   1 +
> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
> 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>@@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
> 	SMA_MODE_OUT,
> };
> 
>+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {

const

>+	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 {
>@@ -295,6 +305,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;
>@@ -351,8 +362,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)
>@@ -836,6 +848,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[] = {
>@@ -856,31 +869,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 },
>@@ -891,15 +904,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 },
> 	{ }
> };
> 
>@@ -2283,22 +2296,34 @@ 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 = {

Why don't you have this as static const outside the function?


>+		.label = NULL,

Pointless 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.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;
> 	}
>@@ -2308,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);
>@@ -2330,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;
>@@ -2397,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);
> }
>@@ -2446,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 = {
>+		.label = NULL,
>+		.type = DPLL_PIN_TYPE_EXT,
>+		.capabilities = 0,

Same comment as to the similar prop struct above. Plus another pointless
init here.


>+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+		.freq_supported = ptp_ocp_sma_freq,
>+
>+	};
> 	u32 reg;
> 	int i;
> 
>@@ -2460,16 +2493,16 @@ 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.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) {
>@@ -2480,9 +2513,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;
> 		}
> 	}
>@@ -2549,6 +2586,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);
>@@ -2690,16 +2730,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;
>@@ -2734,6 +2767,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)
>@@ -4172,12 +4219,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;

Does your device support event delivery in case of the status change?
ice and mlx5 drivers do poll for changes in this area anyway. It's a
part of this patchset. You should do the same if your device does
not support events.

Could you please implement notifications using
dpll_device_notify() for status change and dpll_pin_notify() for pin
state change?



>+
>+	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_SOURCE :
>+				  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)

I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
is not set and therefore the check in dpll_pin_direction_set() will be
true and -EOPNOTSUPP will be returned from there.
Remove this.


>+		return -EOPNOTSUPP;
>+	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>+			    SMA_MODE_IN : SMA_MODE_OUT;
>+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);

You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
to accept struct ptp_ocp_sma_connector * instead avoiding the need for
tne sma_nr completely?


>+}
>+
>+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)

In that case, just fill up a single frequency in the properties,
avoid this check-fail and let the dpll core handle it.


>+		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);

1) void "()"s here.
2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
   this easier to follow? IDK, just a suggestion, take or leave.

Same applies to the the rest of similar occurances above.


>+	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,

This op is a leftover, in dpll core it is not called. This was removed
and agreed that drivers should implement state_on_dpll_get() op for pins
to see which one is connected.

Please fix here and remove the leftover from DPLL patch #2 as well.


>+	.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) {
>@@ -4227,8 +4410,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);

I suggested this the last time, but again: Could you please:
1) rename dpll_device_get to __dpll_device_get
2) introduce dpll_device_get as a macro filling up THIS_MODULE

Then drivers will just call always:
bp->dpll = dpll_device_get(clkid, 0);
and the macro will fillup the module automatically.

Please do the same for dpll_pin_get()


>+	if (IS_ERR(bp->dpll)) {
>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");

You need to fix your error path to call devlink_unregister() in this
case.


>+		goto out;
>+	}
>+
>+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>+	if (err)

You need to fix your error path to call dpll_device_put() in this
case.


>+		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], NULL);
>+		if (err) {
>+			dpll_pin_put(bp->sma[i].dpll_pin);
>+			goto out_dpll;
>+		}
>+	}
>+
>+	return 0;
>+out_dpll:
>+	while (i) {
>+		--i;

	while (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:
>@@ -4243,7 +4457,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) {

Remove this pointless check. It is always true.


>+			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.34.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04 11:18     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04 11:18 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:

[...]


>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>index e188bc189754..75eeaa4396eb 100644
>--- a/include/uapi/linux/dpll.h
>+++ b/include/uapi/linux/dpll.h
>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> 
> #define DPLL_PIN_FREQUENCY_1_HZ		1
> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500

This should be moved to patch #1.
please convert to enum, could be unnamed.

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-04 11:18     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04 11:18 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:

[...]


>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>index e188bc189754..75eeaa4396eb 100644
>--- a/include/uapi/linux/dpll.h
>+++ b/include/uapi/linux/dpll.h
>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> 
> #define DPLL_PIN_FREQUENCY_1_HZ		1
> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500

This should be moved to patch #1.
please convert to enum, could be unnamed.

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04 12:02     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04 12:02 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

Fri, Apr 28, 2023 at 02:20:02AM CEST, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Add a protocol spec for DPLL.
>Add code generated from the spec.
>
>Signed-off-by: Jakub Kicinski <kuba@kernel.org>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
> drivers/dpll/dpll_nl.c                | 126 +++++++
> drivers/dpll/dpll_nl.h                |  42 +++
> include/uapi/linux/dpll.h             | 202 +++++++++++
> 4 files changed, 842 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..67ca0f6cf2d5
>--- /dev/null
>+++ b/Documentation/netlink/specs/dpll.yaml
>@@ -0,0 +1,472 @@
>+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
>+      attribute
>+    entries:
>+      -
>+        name: unspec

In general, why exactly do we need unspec values in enums and CMDs?
What is the usecase. If there isn't please remove.


>+        doc: unspecified value
>+      -
>+        name: manual
>+        doc: source can be only selected by sending a request to dpll
>+      -
>+        name: automatic
>+        doc: highest prio, valid source, auto selected by dpll
>+      -
>+        name: holdover
>+        doc: dpll forced into holdover mode
>+      -
>+        name: freerun
>+        doc: dpll driven on system clk, no holdover available

Remove "no holdover available". This is not a state, this is a mode
configuration. If holdover is or isn't available, is a runtime info.


>+      -
>+        name: nco
>+        doc: dpll driven by Numerically Controlled Oscillator
>+    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: unspec
>+        doc: unspecified value
>+      -
>+        name: unlocked
>+        doc: |
>+          dpll was not yet locked to any valid source (or is in one of
>+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>+      -
>+        name: calibrating
>+        doc: dpll is trying to lock to a valid signal
>+      -
>+        name: locked
>+        doc: dpll is locked
>+      -
>+        name: holdover
>+        doc: |
>+          dpll is in holdover state - lost a valid lock or was forced by
>+          selecting DPLL_MODE_HOLDOVER mode

Is it needed to mention the holdover mode. It's slightly confusing,
because user might understand that the lock-status is always "holdover"
in case of "holdover" mode. But it could be "unlocked", can't it?
Perhaps I don't understand the flows there correctly :/


>+    render-max: true
>+  -
>+    type: const
>+    name: temp-divider
>+    value: 10
>+    doc: |
>+      temperature divider allowing userspace to calculate the
>+      temperature as float with single digit precision.
>+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>+      tempearture value.

s/tempearture/temperature/

Didn't checkpatch warn you?


>+      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: unspec
>+        doc: unspecified value
>+      -
>+        name: pps
>+        doc: dpll produces Pulse-Per-Second signal
>+      -
>+        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: unspec
>+        doc: unspecified value
>+      -
>+        name: mux
>+        doc: aggregates another layer of selectable pins
>+      -
>+        name: ext
>+        doc: external source
>+      -
>+        name: synce-eth-port
>+        doc: ethernet port PHY's recovered clock
>+      -
>+        name: int-oscillator
>+        doc: device internal oscillator

Is this somehow related to the mode "nco" (Numerically Controlled
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: unspec
>+        doc: unspecified value
>+      -
>+        name: source
>+        doc: pin used as a source of a signal
>+      -
>+        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-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: unspec
>+        doc: unspecified value
>+      -
>+        name: connected
>+        doc: pin connected, active source of phase locked loop
>+      -
>+        name: disconnected
>+        doc: pin disconnected, not considered as a valid source
>+      -
>+        name: selectable
>+        doc: pin enabled for automatic source 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
>+  -
>+    type: enum
>+    name: event
>+    doc: events of dpll generic netlink family
>+    entries:
>+      -
>+        name: unspec
>+        doc: invalid event type
>+      -
>+        name: device-create
>+        doc: dpll device created
>+      -
>+        name: device-delete
>+        doc: dpll device deleted
>+      -
>+        name: device-change

Please have a separate create/delete/change values for pins.


>+        doc: |
>+          attribute of dpll device or pin changed, reason is to be found with
>+          an attribute type (DPLL_A_*) received with the event
>+
>+
>+attribute-sets:
>+  -
>+    name: dpll
>+    enum-name: dplla
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1

Why not 0?

Also, Plese don't have this attr as a first one. It is related to
PIN_GET/SET cmd, it should be somewhere among related attributes.

Definitelly, the handle ATTR/ATTTs should be the first one/ones.



>+        multi-attr: true
>+        nested-attributes: device
>+      -
>+        name: id
>+        type: u32
>+      -
>+        name: dev-name
>+        type: string
>+      -
>+        name: bus-name
>+        type: string
>+      -
>+        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: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin-idx
>+        type: u32
>+      -
>+        name: pin-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-parent
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin-parent
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+      -
>+        name: pin-rclk-device
>+        type: string
>+      -
>+        name: pin-dpll-caps
>+        type: u32
>+  -
>+    name: device
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: id
>+        type: u32
>+        value: 2
>+      -
>+        name: dev-name
>+        type: string
>+      -
>+        name: bus-name
>+        type: string
>+      -
>+        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: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin-prio
>+        type: u32
>+        value: 19

Do you still need to pass values for a subset? That is odd. Well, I
think is is odd to pass anything other than names in subset definition,
the rest of the info is in the original attribute set definition,
isn't it?
Jakub?


>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+  -
>+    name: pin-parent
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-state
>+        type: u8
>+        value: 20
>+        enum: pin-state
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+        value: 22
>+      -
>+        name: pin-rclk-device
>+        type: string
>+  -
>+    name: pin-frequency-range
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-frequency-min
>+        type: u64
>+        value: 17
>+      -
>+        name: pin-frequency-max
>+        type: u64
>+
>+operations:
>+  list:
>+    -
>+      name: unspec
>+      doc: unused
>+
>+    -
>+      name: device-get
>+      doc: |
>+        Get list of DPLL devices (dump) or attributes of a single dpll device
>+      attribute-set: dpll
>+      flags: [ admin-perm ]

I may be missing something, but why do you enforce adming perm for
get/dump cmds?


>+
>+      do:
>+        pre: dpll-pre-doit
>+        post: dpll-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        reply:
>+          attributes:
>+            - device
>+
>+      dump:
>+        pre: dpll-pre-dumpit
>+        post: dpll-post-dumpit
>+        reply:
>+          attributes:
>+            - device

I might be missing something, but this means "device" netdev attribute
DPLL_A_DEVICE, right? If yes, that is incorrect and you should list all
the device 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
>+            - bus-name
>+            - dev-name
>+            - mode
>+
>+    -
>+      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
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+        reply: &pin-attrs
>+          attributes:
>+            - pin-idx
>+            - pin-label
>+            - pin-type
>+            - pin-direction
>+            - pin-frequency
>+            - pin-frequency-supported
>+            - pin-parent
>+            - pin-rclk-device
>+            - pin-dpll-caps
>+            - device
>+
>+      dump:
>+        pre: dpll-pin-pre-dumpit
>+        post: dpll-pin-post-dumpit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        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
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+            - pin-frequency
>+            - pin-direction
>+            - pin-prio
>+            - pin-state
>+            - pin-parent-idx
>+
>+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..2f8643f401b0
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.c
>@@ -0,0 +1,126 @@
>+// 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 <linux/dpll.h>
>+
>+/* DPLL_CMD_DEVICE_GET - do */
>+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_MODE] = NLA_POLICY_MAX(NLA_U8, 5),

I know it is a matter of the generator script, still have to note it
hurts my eyes to see "5" here :)


>+};
>+
>+/* DPLL_CMD_PIN_GET - do */
>+static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_IDX + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
>+};
>+
>+/* DPLL_CMD_PIN_GET - dump */
>+static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+};
>+
>+/* DPLL_CMD_PIN_SET - do */
>+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
>+};
>+
>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[] = {
>+	{
>+		.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_BUS_NAME,
>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_GET,
>+		.start	= dpll_pre_dumpit,
>+		.dumpit	= dpll_nl_device_get_dumpit,
>+		.done	= dpll_post_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_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_IDX,
>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>+	},
>+	{
>+		.cmd		= DPLL_CMD_PIN_GET,
>+		.start		= dpll_pin_pre_dumpit,
>+		.dumpit		= dpll_nl_pin_get_dumpit,
>+		.done		= dpll_pin_post_dumpit,
>+		.policy		= dpll_pin_get_dump_nl_policy,
>+		.maxattr	= DPLL_A_BUS_NAME,
>+		.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_IDX,
>+		.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..57ab2da562ba
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.h
>@@ -0,0 +1,42 @@
>+/* 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 <linux/dpll.h>
>+
>+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_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_pre_dumpit(struct netlink_callback *cb);
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
>+int dpll_post_dumpit(struct netlink_callback *cb);
>+int dpll_pin_post_dumpit(struct netlink_callback *cb);
>+
>+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_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..e188bc189754
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,202 @@
>+/* 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 sources to syntonize with it, valid values for
>+ *   DPLL_A_MODE attribute
>+ * @DPLL_MODE_UNSPEC: unspecified value
>+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to dpll
>+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
>+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
>+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
>+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
>+ */
>+enum dpll_mode {
>+	DPLL_MODE_UNSPEC,
>+	DPLL_MODE_MANUAL,
>+	DPLL_MODE_AUTOMATIC,
>+	DPLL_MODE_HOLDOVER,
>+	DPLL_MODE_FREERUN,
>+	DPLL_MODE_NCO,
>+
>+	__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_UNSPEC: unspecified value
>+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid source (or
>+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid signal
>+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
>+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid lock or
>+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
>+ */
>+enum dpll_lock_status {
>+	DPLL_LOCK_STATUS_UNSPEC,
>+	DPLL_LOCK_STATUS_UNLOCKED,
>+	DPLL_LOCK_STATUS_CALIBRATING,
>+	DPLL_LOCK_STATUS_LOCKED,
>+	DPLL_LOCK_STATUS_HOLDOVER,
>+
>+	__DPLL_LOCK_STATUS_MAX,
>+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
>+};
>+
>+#define DPLL_TEMP_DIVIDER	10
>+
>+/**
>+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
>+ * @DPLL_TYPE_UNSPEC: unspecified value
>+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
>+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
>+ */
>+enum dpll_type {
>+	DPLL_TYPE_UNSPEC,
>+	DPLL_TYPE_PPS,
>+	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_UNSPEC: unspecified value
>+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
>+ * @DPLL_PIN_TYPE_EXT: external source
>+ * @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_UNSPEC,
>+	DPLL_PIN_TYPE_MUX,
>+	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_UNSPEC: unspecified value
>+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
>+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
>+ */
>+enum dpll_pin_direction {
>+	DPLL_PIN_DIRECTION_UNSPEC,
>+	DPLL_PIN_DIRECTION_SOURCE,
>+	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_MHZ	10000000
>+
>+/**
>+ * enum dpll_pin_state - defines possible states of a pin, valid values for
>+ *   DPLL_A_PIN_STATE attribute
>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase locked loop
>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
>+ *   source
>+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
>+ */
>+enum dpll_pin_state {
>+	DPLL_PIN_STATE_UNSPEC,
>+	DPLL_PIN_STATE_CONNECTED,
>+	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_event - events of dpll generic netlink family
>+ * @DPLL_EVENT_UNSPEC: invalid event type
>+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
>+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
>+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed, reason
>+ *   is to be found with an attribute type (DPLL_A_*) received with the event
>+ */
>+enum dpll_event {
>+	DPLL_EVENT_UNSPEC,
>+	DPLL_EVENT_DEVICE_CREATE,
>+	DPLL_EVENT_DEVICE_DELETE,
>+	DPLL_EVENT_DEVICE_CHANGE,
>+};
>+
>+enum dplla {
>+	DPLL_A_DEVICE = 1,
>+	DPLL_A_ID,
>+	DPLL_A_DEV_NAME,
>+	DPLL_A_BUS_NAME,
>+	DPLL_A_MODE,
>+	DPLL_A_MODE_SUPPORTED,
>+	DPLL_A_LOCK_STATUS,
>+	DPLL_A_TEMP,
>+	DPLL_A_CLOCK_ID,
>+	DPLL_A_TYPE,
>+	DPLL_A_PIN_IDX,
>+	DPLL_A_PIN_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_PARENT,
>+	DPLL_A_PIN_PARENT_IDX,
>+	DPLL_A_PIN_RCLK_DEVICE,
>+	DPLL_A_PIN_DPLL_CAPS,
>+
>+	__DPLL_A_MAX,
>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>+};
>+
>+enum {
>+	DPLL_CMD_UNSPEC = 1,
>+	DPLL_CMD_DEVICE_GET,
>+	DPLL_CMD_DEVICE_SET,
>+	DPLL_CMD_PIN_GET,
>+	DPLL_CMD_PIN_SET,
>+
>+	__DPLL_CMD_MAX,
>+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
>+};
>+
>+#define DPLL_MCGRP_MONITOR	"monitor"
>+
>+#endif /* _UAPI_LINUX_DPLL_H */
>-- 
>2.34.1
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-04 12:02     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-04 12:02 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

Fri, Apr 28, 2023 at 02:20:02AM CEST, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Add a protocol spec for DPLL.
>Add code generated from the spec.
>
>Signed-off-by: Jakub Kicinski <kuba@kernel.org>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
> drivers/dpll/dpll_nl.c                | 126 +++++++
> drivers/dpll/dpll_nl.h                |  42 +++
> include/uapi/linux/dpll.h             | 202 +++++++++++
> 4 files changed, 842 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..67ca0f6cf2d5
>--- /dev/null
>+++ b/Documentation/netlink/specs/dpll.yaml
>@@ -0,0 +1,472 @@
>+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
>+      attribute
>+    entries:
>+      -
>+        name: unspec

In general, why exactly do we need unspec values in enums and CMDs?
What is the usecase. If there isn't please remove.


>+        doc: unspecified value
>+      -
>+        name: manual
>+        doc: source can be only selected by sending a request to dpll
>+      -
>+        name: automatic
>+        doc: highest prio, valid source, auto selected by dpll
>+      -
>+        name: holdover
>+        doc: dpll forced into holdover mode
>+      -
>+        name: freerun
>+        doc: dpll driven on system clk, no holdover available

Remove "no holdover available". This is not a state, this is a mode
configuration. If holdover is or isn't available, is a runtime info.


>+      -
>+        name: nco
>+        doc: dpll driven by Numerically Controlled Oscillator
>+    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: unspec
>+        doc: unspecified value
>+      -
>+        name: unlocked
>+        doc: |
>+          dpll was not yet locked to any valid source (or is in one of
>+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>+      -
>+        name: calibrating
>+        doc: dpll is trying to lock to a valid signal
>+      -
>+        name: locked
>+        doc: dpll is locked
>+      -
>+        name: holdover
>+        doc: |
>+          dpll is in holdover state - lost a valid lock or was forced by
>+          selecting DPLL_MODE_HOLDOVER mode

Is it needed to mention the holdover mode. It's slightly confusing,
because user might understand that the lock-status is always "holdover"
in case of "holdover" mode. But it could be "unlocked", can't it?
Perhaps I don't understand the flows there correctly :/


>+    render-max: true
>+  -
>+    type: const
>+    name: temp-divider
>+    value: 10
>+    doc: |
>+      temperature divider allowing userspace to calculate the
>+      temperature as float with single digit precision.
>+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>+      tempearture value.

s/tempearture/temperature/

Didn't checkpatch warn you?


>+      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: unspec
>+        doc: unspecified value
>+      -
>+        name: pps
>+        doc: dpll produces Pulse-Per-Second signal
>+      -
>+        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: unspec
>+        doc: unspecified value
>+      -
>+        name: mux
>+        doc: aggregates another layer of selectable pins
>+      -
>+        name: ext
>+        doc: external source
>+      -
>+        name: synce-eth-port
>+        doc: ethernet port PHY's recovered clock
>+      -
>+        name: int-oscillator
>+        doc: device internal oscillator

Is this somehow related to the mode "nco" (Numerically Controlled
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: unspec
>+        doc: unspecified value
>+      -
>+        name: source
>+        doc: pin used as a source of a signal
>+      -
>+        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-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: unspec
>+        doc: unspecified value
>+      -
>+        name: connected
>+        doc: pin connected, active source of phase locked loop
>+      -
>+        name: disconnected
>+        doc: pin disconnected, not considered as a valid source
>+      -
>+        name: selectable
>+        doc: pin enabled for automatic source 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
>+  -
>+    type: enum
>+    name: event
>+    doc: events of dpll generic netlink family
>+    entries:
>+      -
>+        name: unspec
>+        doc: invalid event type
>+      -
>+        name: device-create
>+        doc: dpll device created
>+      -
>+        name: device-delete
>+        doc: dpll device deleted
>+      -
>+        name: device-change

Please have a separate create/delete/change values for pins.


>+        doc: |
>+          attribute of dpll device or pin changed, reason is to be found with
>+          an attribute type (DPLL_A_*) received with the event
>+
>+
>+attribute-sets:
>+  -
>+    name: dpll
>+    enum-name: dplla
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1

Why not 0?

Also, Plese don't have this attr as a first one. It is related to
PIN_GET/SET cmd, it should be somewhere among related attributes.

Definitelly, the handle ATTR/ATTTs should be the first one/ones.



>+        multi-attr: true
>+        nested-attributes: device
>+      -
>+        name: id
>+        type: u32
>+      -
>+        name: dev-name
>+        type: string
>+      -
>+        name: bus-name
>+        type: string
>+      -
>+        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: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin-idx
>+        type: u32
>+      -
>+        name: pin-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-parent
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin-parent
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+      -
>+        name: pin-rclk-device
>+        type: string
>+      -
>+        name: pin-dpll-caps
>+        type: u32
>+  -
>+    name: device
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: id
>+        type: u32
>+        value: 2
>+      -
>+        name: dev-name
>+        type: string
>+      -
>+        name: bus-name
>+        type: string
>+      -
>+        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: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin-prio
>+        type: u32
>+        value: 19

Do you still need to pass values for a subset? That is odd. Well, I
think is is odd to pass anything other than names in subset definition,
the rest of the info is in the original attribute set definition,
isn't it?
Jakub?


>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+  -
>+    name: pin-parent
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-state
>+        type: u8
>+        value: 20
>+        enum: pin-state
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+        value: 22
>+      -
>+        name: pin-rclk-device
>+        type: string
>+  -
>+    name: pin-frequency-range
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-frequency-min
>+        type: u64
>+        value: 17
>+      -
>+        name: pin-frequency-max
>+        type: u64
>+
>+operations:
>+  list:
>+    -
>+      name: unspec
>+      doc: unused
>+
>+    -
>+      name: device-get
>+      doc: |
>+        Get list of DPLL devices (dump) or attributes of a single dpll device
>+      attribute-set: dpll
>+      flags: [ admin-perm ]

I may be missing something, but why do you enforce adming perm for
get/dump cmds?


>+
>+      do:
>+        pre: dpll-pre-doit
>+        post: dpll-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        reply:
>+          attributes:
>+            - device
>+
>+      dump:
>+        pre: dpll-pre-dumpit
>+        post: dpll-post-dumpit
>+        reply:
>+          attributes:
>+            - device

I might be missing something, but this means "device" netdev attribute
DPLL_A_DEVICE, right? If yes, that is incorrect and you should list all
the device 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
>+            - bus-name
>+            - dev-name
>+            - mode
>+
>+    -
>+      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
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+        reply: &pin-attrs
>+          attributes:
>+            - pin-idx
>+            - pin-label
>+            - pin-type
>+            - pin-direction
>+            - pin-frequency
>+            - pin-frequency-supported
>+            - pin-parent
>+            - pin-rclk-device
>+            - pin-dpll-caps
>+            - device
>+
>+      dump:
>+        pre: dpll-pin-pre-dumpit
>+        post: dpll-pin-post-dumpit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        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
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+            - pin-frequency
>+            - pin-direction
>+            - pin-prio
>+            - pin-state
>+            - pin-parent-idx
>+
>+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..2f8643f401b0
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.c
>@@ -0,0 +1,126 @@
>+// 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 <linux/dpll.h>
>+
>+/* DPLL_CMD_DEVICE_GET - do */
>+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_MODE] = NLA_POLICY_MAX(NLA_U8, 5),

I know it is a matter of the generator script, still have to note it
hurts my eyes to see "5" here :)


>+};
>+
>+/* DPLL_CMD_PIN_GET - do */
>+static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_IDX + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
>+};
>+
>+/* DPLL_CMD_PIN_GET - dump */
>+static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+};
>+
>+/* DPLL_CMD_PIN_SET - do */
>+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
>+	[DPLL_A_ID] = { .type = NLA_U32, },
>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
>+};
>+
>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[] = {
>+	{
>+		.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_BUS_NAME,
>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_GET,
>+		.start	= dpll_pre_dumpit,
>+		.dumpit	= dpll_nl_device_get_dumpit,
>+		.done	= dpll_post_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_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_IDX,
>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>+	},
>+	{
>+		.cmd		= DPLL_CMD_PIN_GET,
>+		.start		= dpll_pin_pre_dumpit,
>+		.dumpit		= dpll_nl_pin_get_dumpit,
>+		.done		= dpll_pin_post_dumpit,
>+		.policy		= dpll_pin_get_dump_nl_policy,
>+		.maxattr	= DPLL_A_BUS_NAME,
>+		.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_IDX,
>+		.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..57ab2da562ba
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.h
>@@ -0,0 +1,42 @@
>+/* 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 <linux/dpll.h>
>+
>+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_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_pre_dumpit(struct netlink_callback *cb);
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
>+int dpll_post_dumpit(struct netlink_callback *cb);
>+int dpll_pin_post_dumpit(struct netlink_callback *cb);
>+
>+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_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..e188bc189754
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,202 @@
>+/* 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 sources to syntonize with it, valid values for
>+ *   DPLL_A_MODE attribute
>+ * @DPLL_MODE_UNSPEC: unspecified value
>+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to dpll
>+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
>+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
>+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
>+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
>+ */
>+enum dpll_mode {
>+	DPLL_MODE_UNSPEC,
>+	DPLL_MODE_MANUAL,
>+	DPLL_MODE_AUTOMATIC,
>+	DPLL_MODE_HOLDOVER,
>+	DPLL_MODE_FREERUN,
>+	DPLL_MODE_NCO,
>+
>+	__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_UNSPEC: unspecified value
>+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid source (or
>+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid signal
>+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
>+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid lock or
>+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
>+ */
>+enum dpll_lock_status {
>+	DPLL_LOCK_STATUS_UNSPEC,
>+	DPLL_LOCK_STATUS_UNLOCKED,
>+	DPLL_LOCK_STATUS_CALIBRATING,
>+	DPLL_LOCK_STATUS_LOCKED,
>+	DPLL_LOCK_STATUS_HOLDOVER,
>+
>+	__DPLL_LOCK_STATUS_MAX,
>+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
>+};
>+
>+#define DPLL_TEMP_DIVIDER	10
>+
>+/**
>+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
>+ * @DPLL_TYPE_UNSPEC: unspecified value
>+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
>+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
>+ */
>+enum dpll_type {
>+	DPLL_TYPE_UNSPEC,
>+	DPLL_TYPE_PPS,
>+	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_UNSPEC: unspecified value
>+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
>+ * @DPLL_PIN_TYPE_EXT: external source
>+ * @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_UNSPEC,
>+	DPLL_PIN_TYPE_MUX,
>+	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_UNSPEC: unspecified value
>+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
>+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
>+ */
>+enum dpll_pin_direction {
>+	DPLL_PIN_DIRECTION_UNSPEC,
>+	DPLL_PIN_DIRECTION_SOURCE,
>+	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_MHZ	10000000
>+
>+/**
>+ * enum dpll_pin_state - defines possible states of a pin, valid values for
>+ *   DPLL_A_PIN_STATE attribute
>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase locked loop
>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
>+ *   source
>+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
>+ */
>+enum dpll_pin_state {
>+	DPLL_PIN_STATE_UNSPEC,
>+	DPLL_PIN_STATE_CONNECTED,
>+	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_event - events of dpll generic netlink family
>+ * @DPLL_EVENT_UNSPEC: invalid event type
>+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
>+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
>+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed, reason
>+ *   is to be found with an attribute type (DPLL_A_*) received with the event
>+ */
>+enum dpll_event {
>+	DPLL_EVENT_UNSPEC,
>+	DPLL_EVENT_DEVICE_CREATE,
>+	DPLL_EVENT_DEVICE_DELETE,
>+	DPLL_EVENT_DEVICE_CHANGE,
>+};
>+
>+enum dplla {
>+	DPLL_A_DEVICE = 1,
>+	DPLL_A_ID,
>+	DPLL_A_DEV_NAME,
>+	DPLL_A_BUS_NAME,
>+	DPLL_A_MODE,
>+	DPLL_A_MODE_SUPPORTED,
>+	DPLL_A_LOCK_STATUS,
>+	DPLL_A_TEMP,
>+	DPLL_A_CLOCK_ID,
>+	DPLL_A_TYPE,
>+	DPLL_A_PIN_IDX,
>+	DPLL_A_PIN_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_PARENT,
>+	DPLL_A_PIN_PARENT_IDX,
>+	DPLL_A_PIN_RCLK_DEVICE,
>+	DPLL_A_PIN_DPLL_CAPS,
>+
>+	__DPLL_A_MAX,
>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>+};
>+
>+enum {
>+	DPLL_CMD_UNSPEC = 1,
>+	DPLL_CMD_DEVICE_GET,
>+	DPLL_CMD_DEVICE_SET,
>+	DPLL_CMD_PIN_GET,
>+	DPLL_CMD_PIN_SET,
>+
>+	__DPLL_CMD_MAX,
>+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
>+};
>+
>+#define DPLL_MCGRP_MONITOR	"monitor"
>+
>+#endif /* _UAPI_LINUX_DPLL_H */
>-- 
>2.34.1
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04 19:04     ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 19:04 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:04 -0700 Vadim Fedorenko wrote:
> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> 
> 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 <vadim.fedorenko@linux.dev>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

nit: let's put documentation as patch 1, it's more natural for 
a reviewer to start from the docs.

> diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
> new file mode 100644
> index 000000000000..fba5bc027967
> --- /dev/null
> +++ b/Documentation/dpll.rst
> @@ -0,0 +1,408 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===============================
> +The Linux kernel dpll subsystem
> +===============================
> +
> +

nit: two empty lines here...

> +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
> +=============

.. but none after the section start? One nl to separate things, please.

> +Single dpll device object means single Digital PLL circuit and bunch of
> +pins connected with it.

s/bunch of pins connected with it/bunch of connected pins/

> +It provides its supported working modes and current status to the user

"reports the supported modes of operation" ?

> +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 same command.
> +Requesting configuration of dpll device is done with `do` request of

Changing the configuration ?

> +netlink ``DPLL_CMD_DEVICE_SET`` command.
> +
> +
> +Pin object
> +==========
> +A pin is amorphic object which represents either source or output, it

Is the terminology mixing on purpose?
Source or drain vs input or output ?

> +could be internal component of the device, as well as externaly
> +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 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 manualy or automatically, depending on

manually, spellcheck?

> +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
> +  source for automatic selection algorithm
> +- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
> +  a valid source 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 sources.

But there's a lot more modes in the mode enum :S

> +Shared pins
> +===========
> +A single pin object can be registered to multiple dpll devices.

s/registered/attached/ ?

> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),

Why is direction set on a pin? We can't chain DPLLs?

> +2) Set on a pin-dpll tuple - the configuration affects only selected
> +   dpll device. (i.e. PIN_PRIO, PIN_STATE).
> +
> +
> +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': [{
> +        'device': [{'bus-name': 'pci',
> +                    'dev-name': '0000:21:00.0_0', 'id': 0}],
> +        'pin-direction': {'doc': 'pin used as a source of a signal',
> +                          'name': 'source'},
> +        'pin-idx': 13,
> +        'pin-parent': [{'pin-parent-idx': 2,
> +                        'pin-state': {'doc': 'pin disconnected',
> +                                      'name': 'disconnected'}},
> +                       {'pin-parent-idx': 3,
> +                        'pin-state': {'doc': 'pin disconnected',
> +                                      'name': 'disconnected'}}],
> +        }]``
> +
> +Only one child pin can provide it's signal to the parent MUX-type pin at
> +a time, the selection is done with requesting change of child pin state
> +to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
> +index value in ``DPLL_A_PARENT_PIN_IDX``.
> +
> +Pin priority
> +============
> +Some devices might offer a capability of automatic pin selection mode
> +(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
> +Usually such automatic selection is offloaded to the hardware,

offloaded is a dirty word, s/offloaded to/performed by/

> +which means only pins directly connected to the dpll are capable of
> +automatic source pin selection.

s/are capable of/can be used in the/

> +In automatic selection mode, the user cannot manually select a source
> +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 connect with it.

s/connect with it/use it to control the DPLL device/ ?

> +Child pin of MUX-type is not capable of automatic source pin selection,
> +in order to configure a source of a MUX-type pin, the user needs to
> +request desired pin state of the child pin on the parent - it is done
> +with providing additional attribute for pin set state request - index
> +of parent pin he wish to propagate its signal to
> +(``DPLL_A_PARENT_PIN_IDX``).
> +
> +
> +Configuration commands group
> +============================
> +

oh and the new lines after headers appear ;)

> +Configuration commands are used to get or dump information about

s/or dump//
the netlink ability to query or dump is not something specific to dpll,
I wouldn't mention it so many times.

> +registered dpll devices (and pins), as well as set configuration of
> +device or pins. As dpll device could not be abstract and reflects real

Missing an article before dpll device, maybe "each dpll device", or 'a
dppl device' or 'dpll devices" (plural)

s/could/must/

> +hardware, there is no way to add new dpll device via netlink from user
> +space and each device should be registered by it's driver.
> +
> +All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
> +any spamming/D.o.S. from unauthorized userspace applications.

no dots in DoS

> +In general it is possible to configure multiple parameters at once.

s/In general//
Should we say that we don't guarantee the change will be atomic in that
case?

> +Device driver implementation
> +============================
> +
> +Device is allocated by ``dpll_device_get`` call. Second call with the
> +same arguments doesn't create new object but provides pointer to
> +previously created device for given arguments, it also increase 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.

You can add () after the function name and render the kdoc at the end
of this doc. The `` marking will then be unnecessary.



^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
@ 2023-05-04 19:04     ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 19:04 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:04 -0700 Vadim Fedorenko wrote:
> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> 
> 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 <vadim.fedorenko@linux.dev>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

nit: let's put documentation as patch 1, it's more natural for 
a reviewer to start from the docs.

> diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
> new file mode 100644
> index 000000000000..fba5bc027967
> --- /dev/null
> +++ b/Documentation/dpll.rst
> @@ -0,0 +1,408 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===============================
> +The Linux kernel dpll subsystem
> +===============================
> +
> +

nit: two empty lines here...

> +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
> +=============

.. but none after the section start? One nl to separate things, please.

> +Single dpll device object means single Digital PLL circuit and bunch of
> +pins connected with it.

s/bunch of pins connected with it/bunch of connected pins/

> +It provides its supported working modes and current status to the user

"reports the supported modes of operation" ?

> +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 same command.
> +Requesting configuration of dpll device is done with `do` request of

Changing the configuration ?

> +netlink ``DPLL_CMD_DEVICE_SET`` command.
> +
> +
> +Pin object
> +==========
> +A pin is amorphic object which represents either source or output, it

Is the terminology mixing on purpose?
Source or drain vs input or output ?

> +could be internal component of the device, as well as externaly
> +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 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 manualy or automatically, depending on

manually, spellcheck?

> +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
> +  source for automatic selection algorithm
> +- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
> +  a valid source 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 sources.

But there's a lot more modes in the mode enum :S

> +Shared pins
> +===========
> +A single pin object can be registered to multiple dpll devices.

s/registered/attached/ ?

> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),

Why is direction set on a pin? We can't chain DPLLs?

> +2) Set on a pin-dpll tuple - the configuration affects only selected
> +   dpll device. (i.e. PIN_PRIO, PIN_STATE).
> +
> +
> +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': [{
> +        'device': [{'bus-name': 'pci',
> +                    'dev-name': '0000:21:00.0_0', 'id': 0}],
> +        'pin-direction': {'doc': 'pin used as a source of a signal',
> +                          'name': 'source'},
> +        'pin-idx': 13,
> +        'pin-parent': [{'pin-parent-idx': 2,
> +                        'pin-state': {'doc': 'pin disconnected',
> +                                      'name': 'disconnected'}},
> +                       {'pin-parent-idx': 3,
> +                        'pin-state': {'doc': 'pin disconnected',
> +                                      'name': 'disconnected'}}],
> +        }]``
> +
> +Only one child pin can provide it's signal to the parent MUX-type pin at
> +a time, the selection is done with requesting change of child pin state
> +to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
> +index value in ``DPLL_A_PARENT_PIN_IDX``.
> +
> +Pin priority
> +============
> +Some devices might offer a capability of automatic pin selection mode
> +(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
> +Usually such automatic selection is offloaded to the hardware,

offloaded is a dirty word, s/offloaded to/performed by/

> +which means only pins directly connected to the dpll are capable of
> +automatic source pin selection.

s/are capable of/can be used in the/

> +In automatic selection mode, the user cannot manually select a source
> +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 connect with it.

s/connect with it/use it to control the DPLL device/ ?

> +Child pin of MUX-type is not capable of automatic source pin selection,
> +in order to configure a source of a MUX-type pin, the user needs to
> +request desired pin state of the child pin on the parent - it is done
> +with providing additional attribute for pin set state request - index
> +of parent pin he wish to propagate its signal to
> +(``DPLL_A_PARENT_PIN_IDX``).
> +
> +
> +Configuration commands group
> +============================
> +

oh and the new lines after headers appear ;)

> +Configuration commands are used to get or dump information about

s/or dump//
the netlink ability to query or dump is not something specific to dpll,
I wouldn't mention it so many times.

> +registered dpll devices (and pins), as well as set configuration of
> +device or pins. As dpll device could not be abstract and reflects real

Missing an article before dpll device, maybe "each dpll device", or 'a
dppl device' or 'dpll devices" (plural)

s/could/must/

> +hardware, there is no way to add new dpll device via netlink from user
> +space and each device should be registered by it's driver.
> +
> +All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
> +any spamming/D.o.S. from unauthorized userspace applications.

no dots in DoS

> +In general it is possible to configure multiple parameters at once.

s/In general//
Should we say that we don't guarantee the change will be atomic in that
case?

> +Device driver implementation
> +============================
> +
> +Device is allocated by ``dpll_device_get`` call. Second call with the
> +same arguments doesn't create new object but provides pointer to
> +previously created device for given arguments, it also increase 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.

You can add () after the function name and render the kdoc at the end
of this doc. The `` marking will then be unnecessary.



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-04 11:18     ` Jiri Pirko
@ 2023-05-04 20:27       ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 20:27 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 4 May 2023 13:18:49 +0200 Jiri Pirko wrote:
> >diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
> >index e188bc189754..75eeaa4396eb 100644
> >--- a/include/uapi/linux/dpll.h
> >+++ b/include/uapi/linux/dpll.h
> >@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> > 
> > #define DPLL_PIN_FREQUENCY_1_HZ		1
> > #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
> >+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
> >+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500  
> 
> This should be moved to patch #1.
> please convert to enum, could be unnamed.

+1, you can't edit the YNL-generated files at all.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-04 20:27       ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 20:27 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 4 May 2023 13:18:49 +0200 Jiri Pirko wrote:
> >diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
> >index e188bc189754..75eeaa4396eb 100644
> >--- a/include/uapi/linux/dpll.h
> >+++ b/include/uapi/linux/dpll.h
> >@@ -111,6 +111,8 @@ enum dpll_pin_direction {
> > 
> > #define DPLL_PIN_FREQUENCY_1_HZ		1
> > #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
> >+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
> >+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500  
> 
> This should be moved to patch #1.
> please convert to enum, could be unnamed.

+1, you can't edit the YNL-generated files at all.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04 20:31     ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 20:31 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:08 -0700 Vadim Fedorenko wrote:
> @@ -2411,6 +2412,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

kdoc is missing. I'm guessing that one pin covers all current user
cases but we should clearly document on what this pin is, so that when
we extend the code to support multiple pins (in/out, per lane, idk)
we know which one this was.. ?

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
@ 2023-05-04 20:31     ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 20:31 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Jiri Pirko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:08 -0700 Vadim Fedorenko wrote:
> @@ -2411,6 +2412,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

kdoc is missing. I'm guessing that one pin covers all current user
cases but we should clearly document on what this pin is, so that when
we extend the code to support multiple pins (in/out, per lane, idk)
we know which one this was.. ?

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-04 21:21     ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 21:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:03 -0700 Vadim Fedorenko wrote:
> +/**
> + * struct dpll_pin - structure for a dpll pin
> + * @idx:		unique idx given by alloc on global pin's XA
> + * @dev_driver_id:	id given by dev driver
> + * @clock_id:		clock_id of creator
> + * @module:		module of creator
> + * @dpll_refs:		hold referencees to dplls that pin is registered with
> + * @pin_refs:		hold references to pins that pin is registered with
> + * @prop:		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;
> +	struct dpll_pin_properties prop;
> +	char *rclk_dev_name;
> +	refcount_t refcount;
> +};

The kdoc for structures is quite out of date, please run
./scripts/kernel-doc -none $DPLL_FILES

> +/**
> + * dpll_device_notify - notify on dpll device change
> + * @dpll: dpll device pointer
> + * @attr: changed attribute
> + *
> + * Broadcast event to the netlink multicast registered listeners.
> + *
> + * Return:
> + * * 0 - success
> + * * negative - error
> + */

Let's move the kdoc to the implementation. I believe that's 
the preferred kernel coding style.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-04 21:21     ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 21:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 27 Apr 2023 17:20:03 -0700 Vadim Fedorenko wrote:
> +/**
> + * struct dpll_pin - structure for a dpll pin
> + * @idx:		unique idx given by alloc on global pin's XA
> + * @dev_driver_id:	id given by dev driver
> + * @clock_id:		clock_id of creator
> + * @module:		module of creator
> + * @dpll_refs:		hold referencees to dplls that pin is registered with
> + * @pin_refs:		hold references to pins that pin is registered with
> + * @prop:		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;
> +	struct dpll_pin_properties prop;
> +	char *rclk_dev_name;
> +	refcount_t refcount;
> +};

The kdoc for structures is quite out of date, please run
./scripts/kernel-doc -none $DPLL_FILES

> +/**
> + * dpll_device_notify - notify on dpll device change
> + * @dpll: dpll device pointer
> + * @attr: changed attribute
> + *
> + * Broadcast event to the netlink multicast registered listeners.
> + *
> + * Return:
> + * * 0 - success
> + * * negative - error
> + */

Let's move the kdoc to the implementation. I believe that's 
the preferred kernel coding style.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-04 12:02     ` Jiri Pirko
@ 2023-05-04 21:24       ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 21:24 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
> >+definitions:
> >+  -
> >+    type: enum
> >+    name: mode
> >+    doc: |
> >+      working-modes a dpll can support, differentiate if and how dpll selects
> >+      one of its sources to syntonize with it, valid values for DPLL_A_MODE
> >+      attribute
> >+    entries:
> >+      -
> >+        name: unspec  
> 
> In general, why exactly do we need unspec values in enums and CMDs?
> What is the usecase. If there isn't please remove.

+1

> >+        doc: unspecified value
> >+      -
> >+        name: manual

I think the documentation calls this "forced", still.

> >+        doc: source can be only selected by sending a request to dpll
> >+      -
> >+        name: automatic
> >+        doc: highest prio, valid source, auto selected by dpll
> >+      -
> >+        name: holdover
> >+        doc: dpll forced into holdover mode
> >+      -
> >+        name: freerun
> >+        doc: dpll driven on system clk, no holdover available  
> 
> Remove "no holdover available". This is not a state, this is a mode
> configuration. If holdover is or isn't available, is a runtime info.

Agreed, seems a little confusing now. Should we expose the system clk
as a pin to be able to force lock to it? Or there's some extra magic 
at play here?

> >+      -
> >+        name: nco
> >+        doc: dpll driven by Numerically Controlled Oscillator

Noob question, what is NCO in terms of implementation?
We source the signal from an arbitrary pin and FW / driver does 
the control? Or we always use system refclk and then tune?

> >+    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: unspec
> >+        doc: unspecified value
> >+      -
> >+        name: unlocked
> >+        doc: |
> >+          dpll was not yet locked to any valid source (or is in one of
> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
> >+      -
> >+        name: calibrating
> >+        doc: dpll is trying to lock to a valid signal
> >+      -
> >+        name: locked
> >+        doc: dpll is locked
> >+      -
> >+        name: holdover
> >+        doc: |
> >+          dpll is in holdover state - lost a valid lock or was forced by
> >+          selecting DPLL_MODE_HOLDOVER mode  
> 
> Is it needed to mention the holdover mode. It's slightly confusing,
> because user might understand that the lock-status is always "holdover"
> in case of "holdover" mode. But it could be "unlocked", can't it?
> Perhaps I don't understand the flows there correctly :/

Hm, if we want to make sure that holdover mode must result in holdover
state then we need some extra atomicity requirements on the SET
operation. To me it seems logical enough that after setting holdover
mode we'll end up either in holdover or unlocked status, depending on
lock status when request reached the HW.

> >+    render-max: true
> >+  -
> >+    type: const
> >+    name: temp-divider
> >+    value: 10
> >+    doc: |
> >+      temperature divider allowing userspace to calculate the
> >+      temperature as float with single digit precision.
> >+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
> >+      tempearture value.  
> 
> s/tempearture/temperature/
> 
> Didn't checkpatch warn you?

Also can we give it a more healthy engineering margin?
DPLL_A_TEMP is u32, silicon melts at around 1400C, 
so we really can afford to make the divisor 1000.

> >+    name: device
> >+    subset-of: dpll
> >+    attributes:
> >+      -
> >+        name: id
> >+        type: u32
> >+        value: 2
> >+      -
> >+        name: dev-name
> >+        type: string
> >+      -
> >+        name: bus-name
> >+        type: string
> >+      -
> >+        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: clock-id
> >+        type: u64
> >+      -
> >+        name: type
> >+        type: u8
> >+        enum: type
> >+      -
> >+        name: pin-prio
> >+        type: u32
> >+        value: 19  
> 
> Do you still need to pass values for a subset? That is odd. Well, I
> think is is odd to pass anything other than names in subset definition,
> the rest of the info is in the original attribute set definition,
> isn't it?
> Jakub?

Probably stale code, related bug was fixed in YNL a few months back.
Explicit value should no longer be needed.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-04 21:24       ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-04 21:24 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
> >+definitions:
> >+  -
> >+    type: enum
> >+    name: mode
> >+    doc: |
> >+      working-modes a dpll can support, differentiate if and how dpll selects
> >+      one of its sources to syntonize with it, valid values for DPLL_A_MODE
> >+      attribute
> >+    entries:
> >+      -
> >+        name: unspec  
> 
> In general, why exactly do we need unspec values in enums and CMDs?
> What is the usecase. If there isn't please remove.

+1

> >+        doc: unspecified value
> >+      -
> >+        name: manual

I think the documentation calls this "forced", still.

> >+        doc: source can be only selected by sending a request to dpll
> >+      -
> >+        name: automatic
> >+        doc: highest prio, valid source, auto selected by dpll
> >+      -
> >+        name: holdover
> >+        doc: dpll forced into holdover mode
> >+      -
> >+        name: freerun
> >+        doc: dpll driven on system clk, no holdover available  
> 
> Remove "no holdover available". This is not a state, this is a mode
> configuration. If holdover is or isn't available, is a runtime info.

Agreed, seems a little confusing now. Should we expose the system clk
as a pin to be able to force lock to it? Or there's some extra magic 
at play here?

> >+      -
> >+        name: nco
> >+        doc: dpll driven by Numerically Controlled Oscillator

Noob question, what is NCO in terms of implementation?
We source the signal from an arbitrary pin and FW / driver does 
the control? Or we always use system refclk and then tune?

> >+    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: unspec
> >+        doc: unspecified value
> >+      -
> >+        name: unlocked
> >+        doc: |
> >+          dpll was not yet locked to any valid source (or is in one of
> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
> >+      -
> >+        name: calibrating
> >+        doc: dpll is trying to lock to a valid signal
> >+      -
> >+        name: locked
> >+        doc: dpll is locked
> >+      -
> >+        name: holdover
> >+        doc: |
> >+          dpll is in holdover state - lost a valid lock or was forced by
> >+          selecting DPLL_MODE_HOLDOVER mode  
> 
> Is it needed to mention the holdover mode. It's slightly confusing,
> because user might understand that the lock-status is always "holdover"
> in case of "holdover" mode. But it could be "unlocked", can't it?
> Perhaps I don't understand the flows there correctly :/

Hm, if we want to make sure that holdover mode must result in holdover
state then we need some extra atomicity requirements on the SET
operation. To me it seems logical enough that after setting holdover
mode we'll end up either in holdover or unlocked status, depending on
lock status when request reached the HW.

> >+    render-max: true
> >+  -
> >+    type: const
> >+    name: temp-divider
> >+    value: 10
> >+    doc: |
> >+      temperature divider allowing userspace to calculate the
> >+      temperature as float with single digit precision.
> >+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
> >+      tempearture value.  
> 
> s/tempearture/temperature/
> 
> Didn't checkpatch warn you?

Also can we give it a more healthy engineering margin?
DPLL_A_TEMP is u32, silicon melts at around 1400C, 
so we really can afford to make the divisor 1000.

> >+    name: device
> >+    subset-of: dpll
> >+    attributes:
> >+      -
> >+        name: id
> >+        type: u32
> >+        value: 2
> >+      -
> >+        name: dev-name
> >+        type: string
> >+      -
> >+        name: bus-name
> >+        type: string
> >+      -
> >+        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: clock-id
> >+        type: u64
> >+      -
> >+        name: type
> >+        type: u8
> >+        enum: type
> >+      -
> >+        name: pin-prio
> >+        type: u32
> >+        value: 19  
> 
> Do you still need to pass values for a subset? That is odd. Well, I
> think is is odd to pass anything other than names in subset definition,
> the rest of the info is in the original attribute set definition,
> isn't it?
> Jakub?

Probably stale code, related bug was fixed in YNL a few months back.
Explicit value should no longer be needed.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-04 21:24       ` Jakub Kicinski
@ 2023-05-05 10:29         ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-05 10:29 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 04, 2023 at 11:24:51PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:

[...]

>
>> >+    name: device
>> >+    subset-of: dpll
>> >+    attributes:
>> >+      -
>> >+        name: id
>> >+        type: u32
>> >+        value: 2
>> >+      -
>> >+        name: dev-name
>> >+        type: string
>> >+      -
>> >+        name: bus-name
>> >+        type: string
>> >+      -
>> >+        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: clock-id
>> >+        type: u64
>> >+      -
>> >+        name: type
>> >+        type: u8
>> >+        enum: type
>> >+      -
>> >+        name: pin-prio
>> >+        type: u32
>> >+        value: 19  
>> 
>> Do you still need to pass values for a subset? That is odd. Well, I
>> think is is odd to pass anything other than names in subset definition,
>> the rest of the info is in the original attribute set definition,
>> isn't it?
>> Jakub?
>
>Probably stale code, related bug was fixed in YNL a few months back.
>Explicit value should no longer be needed.

What about the rest, like type, enum, multi-attr etc. Are they needed
for subset? If yes, why?



^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-05 10:29         ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-05 10:29 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 04, 2023 at 11:24:51PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:

[...]

>
>> >+    name: device
>> >+    subset-of: dpll
>> >+    attributes:
>> >+      -
>> >+        name: id
>> >+        type: u32
>> >+        value: 2
>> >+      -
>> >+        name: dev-name
>> >+        type: string
>> >+      -
>> >+        name: bus-name
>> >+        type: string
>> >+      -
>> >+        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: clock-id
>> >+        type: u64
>> >+      -
>> >+        name: type
>> >+        type: u8
>> >+        enum: type
>> >+      -
>> >+        name: pin-prio
>> >+        type: u32
>> >+        value: 19  
>> 
>> Do you still need to pass values for a subset? That is odd. Well, I
>> think is is odd to pass anything other than names in subset definition,
>> the rest of the info is in the original attribute set definition,
>> isn't it?
>> Jakub?
>
>Probably stale code, related bug was fixed in YNL a few months back.
>Explicit value should no longer be needed.

What about the rest, like type, enum, multi-attr etc. Are they needed
for subset? If yes, why?



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
  2023-05-04 20:31     ` Jakub Kicinski
@ 2023-05-05 10:32       ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-05 10:32 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk

Thu, May 04, 2023 at 10:31:40PM CEST, kuba@kernel.org wrote:
>On Thu, 27 Apr 2023 17:20:08 -0700 Vadim Fedorenko wrote:
>> @@ -2411,6 +2412,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
>
>kdoc is missing. I'm guessing that one pin covers all current user
>cases but we should clearly document on what this pin is, so that when
>we extend the code to support multiple pins (in/out, per lane, idk)
>we know which one this was.. ?

Oh, yeah. Will do.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice
@ 2023-05-05 10:32       ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-05 10:32 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Jiri Pirko, poros, mschmidt, netdev, linux-clk

Thu, May 04, 2023 at 10:31:40PM CEST, kuba@kernel.org wrote:
>On Thu, 27 Apr 2023 17:20:08 -0700 Vadim Fedorenko wrote:
>> @@ -2411,6 +2412,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
>
>kdoc is missing. I'm guessing that one pin covers all current user
>cases but we should clearly document on what this pin is, so that when
>we extend the code to support multiple pins (in/out, per lane, idk)
>we know which one this was.. ?

Oh, yeah. Will do.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-05-04 19:04     ` Jakub Kicinski
@ 2023-05-05 13:16       ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-05-05 13:16 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

On 04/05/2023 20:04, Jakub Kicinski wrote:
> On Thu, 27 Apr 2023 17:20:04 -0700 Vadim Fedorenko wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> 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 <vadim.fedorenko@linux.dev>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> 
> nit: let's put documentation as patch 1, it's more natural for
> a reviewer to start from the docs.

Ok, sure, will do it.
And I agree to almost all the comments here, but for some I have, 
questions below.

>> diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
>> new file mode 100644
>> index 000000000000..fba5bc027967
>> --- /dev/null
>> +++ b/Documentation/dpll.rst
>> @@ -0,0 +1,408 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +===============================
>> +The Linux kernel dpll subsystem
>> +===============================
>> +
>> +
> 
> nit: two empty lines here...
> 
>> +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
>> +=============
> 
> .. but none after the section start? One nl to separate things, please.
> 
>> +Single dpll device object means single Digital PLL circuit and bunch of
>> +pins connected with it.
> 
> s/bunch of pins connected with it/bunch of connected pins/
> 
>> +It provides its supported working modes and current status to the user
> 
> "reports the supported modes of operation" ?
> 
>> +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 same command.
>> +Requesting configuration of dpll device is done with `do` request of
> 
> Changing the configuration ?
> 
>> +netlink ``DPLL_CMD_DEVICE_SET`` command.
>> +
>> +
>> +Pin object
>> +==========
>> +A pin is amorphic object which represents either source or output, it
> 
> Is the terminology mixing on purpose?
> Source or drain vs input or output ?
> 
>> +could be internal component of the device, as well as externaly
>> +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 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 manualy or automatically, depending on
> 
> manually, spellcheck?
> 
>> +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
>> +  source for automatic selection algorithm
>> +- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
>> +  a valid source 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 sources.
> 
> But there's a lot more modes in the mode enum :S
> 
>> +Shared pins
>> +===========
>> +A single pin object can be registered to multiple dpll devices.
> 
> s/registered/attached/ ?
> 
>> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
> 
> Why is direction set on a pin? We can't chain DPLLs?

We can chain DPLLs using pins only. We don't have any interface to
configure 2 pins to connect 2 different DPLLs to each other at the same 
time. The configuration should take care of one pin being input and
other one being output. That's why we have direction property attached
to the pin, not the DPLL itself.

>> +2) Set on a pin-dpll tuple - the configuration affects only selected
>> +   dpll device. (i.e. PIN_PRIO, PIN_STATE).
>> +
>> +
>> +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': [{
>> +        'device': [{'bus-name': 'pci',
>> +                    'dev-name': '0000:21:00.0_0', 'id': 0}],
>> +        'pin-direction': {'doc': 'pin used as a source of a signal',
>> +                          'name': 'source'},
>> +        'pin-idx': 13,
>> +        'pin-parent': [{'pin-parent-idx': 2,
>> +                        'pin-state': {'doc': 'pin disconnected',
>> +                                      'name': 'disconnected'}},
>> +                       {'pin-parent-idx': 3,
>> +                        'pin-state': {'doc': 'pin disconnected',
>> +                                      'name': 'disconnected'}}],
>> +        }]``
>> +
>> +Only one child pin can provide it's signal to the parent MUX-type pin at
>> +a time, the selection is done with requesting change of child pin state
>> +to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
>> +index value in ``DPLL_A_PARENT_PIN_IDX``.
>> +
>> +Pin priority
>> +============
>> +Some devices might offer a capability of automatic pin selection mode
>> +(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
>> +Usually such automatic selection is offloaded to the hardware,
> 
> offloaded is a dirty word, s/offloaded to/performed by/
> 
>> +which means only pins directly connected to the dpll are capable of
>> +automatic source pin selection.
> 
> s/are capable of/can be used in the/
> 
>> +In automatic selection mode, the user cannot manually select a source
>> +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 connect with it.
> 
> s/connect with it/use it to control the DPLL device/ ?
> 
>> +Child pin of MUX-type is not capable of automatic source pin selection,
>> +in order to configure a source of a MUX-type pin, the user needs to
>> +request desired pin state of the child pin on the parent - it is done
>> +with providing additional attribute for pin set state request - index
>> +of parent pin he wish to propagate its signal to
>> +(``DPLL_A_PARENT_PIN_IDX``).
>> +
>> +
>> +Configuration commands group
>> +============================
>> +
> 
> oh and the new lines after headers appear ;)
> 
>> +Configuration commands are used to get or dump information about
> 
> s/or dump//
> the netlink ability to query or dump is not something specific to dpll,
> I wouldn't mention it so many times.
> 
>> +registered dpll devices (and pins), as well as set configuration of
>> +device or pins. As dpll device could not be abstract and reflects real
> 
> Missing an article before dpll device, maybe "each dpll device", or 'a
> dppl device' or 'dpll devices" (plural)
> 
> s/could/must/
> 
>> +hardware, there is no way to add new dpll device via netlink from user
>> +space and each device should be registered by it's driver.
>> +
>> +All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
>> +any spamming/D.o.S. from unauthorized userspace applications.
> 
> no dots in DoS
> 
>> +In general it is possible to configure multiple parameters at once.
> 
> s/In general//
> Should we say that we don't guarantee the change will be atomic in that
> case?

I agree, I'll add this explicitly in the next version.

>> +Device driver implementation
>> +============================
>> +
>> +Device is allocated by ``dpll_device_get`` call. Second call with the
>> +same arguments doesn't create new object but provides pointer to
>> +previously created device for given arguments, it also increase 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.
> 
> You can add () after the function name and render the kdoc at the end
> of this doc. The `` marking will then be unnecessary.
> 
Mmm... any examples of such a way of creating documentation? I was
following tls*.rst style, but without copying code-blocks.


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
@ 2023-05-05 13:16       ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-05-05 13:16 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

On 04/05/2023 20:04, Jakub Kicinski wrote:
> On Thu, 27 Apr 2023 17:20:04 -0700 Vadim Fedorenko wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> 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 <vadim.fedorenko@linux.dev>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> 
> nit: let's put documentation as patch 1, it's more natural for
> a reviewer to start from the docs.

Ok, sure, will do it.
And I agree to almost all the comments here, but for some I have, 
questions below.

>> diff --git a/Documentation/dpll.rst b/Documentation/dpll.rst
>> new file mode 100644
>> index 000000000000..fba5bc027967
>> --- /dev/null
>> +++ b/Documentation/dpll.rst
>> @@ -0,0 +1,408 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +===============================
>> +The Linux kernel dpll subsystem
>> +===============================
>> +
>> +
> 
> nit: two empty lines here...
> 
>> +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
>> +=============
> 
> .. but none after the section start? One nl to separate things, please.
> 
>> +Single dpll device object means single Digital PLL circuit and bunch of
>> +pins connected with it.
> 
> s/bunch of pins connected with it/bunch of connected pins/
> 
>> +It provides its supported working modes and current status to the user
> 
> "reports the supported modes of operation" ?
> 
>> +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 same command.
>> +Requesting configuration of dpll device is done with `do` request of
> 
> Changing the configuration ?
> 
>> +netlink ``DPLL_CMD_DEVICE_SET`` command.
>> +
>> +
>> +Pin object
>> +==========
>> +A pin is amorphic object which represents either source or output, it
> 
> Is the terminology mixing on purpose?
> Source or drain vs input or output ?
> 
>> +could be internal component of the device, as well as externaly
>> +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 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 manualy or automatically, depending on
> 
> manually, spellcheck?
> 
>> +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
>> +  source for automatic selection algorithm
>> +- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
>> +  a valid source 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 sources.
> 
> But there's a lot more modes in the mode enum :S
> 
>> +Shared pins
>> +===========
>> +A single pin object can be registered to multiple dpll devices.
> 
> s/registered/attached/ ?
> 
>> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
> 
> Why is direction set on a pin? We can't chain DPLLs?

We can chain DPLLs using pins only. We don't have any interface to
configure 2 pins to connect 2 different DPLLs to each other at the same 
time. The configuration should take care of one pin being input and
other one being output. That's why we have direction property attached
to the pin, not the DPLL itself.

>> +2) Set on a pin-dpll tuple - the configuration affects only selected
>> +   dpll device. (i.e. PIN_PRIO, PIN_STATE).
>> +
>> +
>> +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': [{
>> +        'device': [{'bus-name': 'pci',
>> +                    'dev-name': '0000:21:00.0_0', 'id': 0}],
>> +        'pin-direction': {'doc': 'pin used as a source of a signal',
>> +                          'name': 'source'},
>> +        'pin-idx': 13,
>> +        'pin-parent': [{'pin-parent-idx': 2,
>> +                        'pin-state': {'doc': 'pin disconnected',
>> +                                      'name': 'disconnected'}},
>> +                       {'pin-parent-idx': 3,
>> +                        'pin-state': {'doc': 'pin disconnected',
>> +                                      'name': 'disconnected'}}],
>> +        }]``
>> +
>> +Only one child pin can provide it's signal to the parent MUX-type pin at
>> +a time, the selection is done with requesting change of child pin state
>> +to ``DPLL_PIN_STATE_CONNECTED`` and providing a target MUX-type pin
>> +index value in ``DPLL_A_PARENT_PIN_IDX``.
>> +
>> +Pin priority
>> +============
>> +Some devices might offer a capability of automatic pin selection mode
>> +(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
>> +Usually such automatic selection is offloaded to the hardware,
> 
> offloaded is a dirty word, s/offloaded to/performed by/
> 
>> +which means only pins directly connected to the dpll are capable of
>> +automatic source pin selection.
> 
> s/are capable of/can be used in the/
> 
>> +In automatic selection mode, the user cannot manually select a source
>> +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 connect with it.
> 
> s/connect with it/use it to control the DPLL device/ ?
> 
>> +Child pin of MUX-type is not capable of automatic source pin selection,
>> +in order to configure a source of a MUX-type pin, the user needs to
>> +request desired pin state of the child pin on the parent - it is done
>> +with providing additional attribute for pin set state request - index
>> +of parent pin he wish to propagate its signal to
>> +(``DPLL_A_PARENT_PIN_IDX``).
>> +
>> +
>> +Configuration commands group
>> +============================
>> +
> 
> oh and the new lines after headers appear ;)
> 
>> +Configuration commands are used to get or dump information about
> 
> s/or dump//
> the netlink ability to query or dump is not something specific to dpll,
> I wouldn't mention it so many times.
> 
>> +registered dpll devices (and pins), as well as set configuration of
>> +device or pins. As dpll device could not be abstract and reflects real
> 
> Missing an article before dpll device, maybe "each dpll device", or 'a
> dppl device' or 'dpll devices" (plural)
> 
> s/could/must/
> 
>> +hardware, there is no way to add new dpll device via netlink from user
>> +space and each device should be registered by it's driver.
>> +
>> +All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
>> +any spamming/D.o.S. from unauthorized userspace applications.
> 
> no dots in DoS
> 
>> +In general it is possible to configure multiple parameters at once.
> 
> s/In general//
> Should we say that we don't guarantee the change will be atomic in that
> case?

I agree, I'll add this explicitly in the next version.

>> +Device driver implementation
>> +============================
>> +
>> +Device is allocated by ``dpll_device_get`` call. Second call with the
>> +same arguments doesn't create new object but provides pointer to
>> +previously created device for given arguments, it also increase 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.
> 
> You can add () after the function name and render the kdoc at the end
> of this doc. The `` marking will then be unnecessary.
> 
Mmm... any examples of such a way of creating documentation? I was
following tls*.rst style, but without copying code-blocks.


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
  2023-05-04  9:27     ` Jiri Pirko
@ 2023-05-05 13:43       ` Vadim Fedorenko
  -1 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-05-05 13:43 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

On 04/05/2023 10:27, Jiri Pirko wrote:
> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> Implement basic DPLL operations in ptp_ocp driver as the
>> simplest example of using new subsystem.
>>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> ---
>> drivers/ptp/Kconfig   |   1 +
>> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>> @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> 	SMA_MODE_OUT,
>> };
>>
>> +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
> 
> const

Forgot about this one, will change it.

> 
>> +	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 {
>> @@ -295,6 +305,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;
>> @@ -351,8 +362,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)
>> @@ -836,6 +848,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[] = {
>> @@ -856,31 +869,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 },
>> @@ -891,15 +904,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 },
>> 	{ }
>> };
>>
>> @@ -2283,22 +2296,34 @@ 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 = {
> 
> Why don't you have this as static const outside the function?
> 

Because I'm changing label string for every pin. I cannot change it in
the const object.

> 
>> +		.label = NULL,
> 
> Pointless init.

Agree

>> +		.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.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;
>> 	}
>> @@ -2308,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);
>> @@ -2330,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;
>> @@ -2397,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);
>> }
>> @@ -2446,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 = {
>> +		.label = NULL,
>> +		.type = DPLL_PIN_TYPE_EXT,
>> +		.capabilities = 0,
> 
> Same comment as to the similar prop struct above. Plus another pointless
> init here.
> 

Will remove pointless init.

> 
>> +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> +		.freq_supported = ptp_ocp_sma_freq,
>> +
>> +	};
>> 	u32 reg;
>> 	int i;
>>
>> @@ -2460,16 +2493,16 @@ 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.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) {
>> @@ -2480,9 +2513,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;
>> 		}
>> 	}
>> @@ -2549,6 +2586,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);
>> @@ -2690,16 +2730,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;
>> @@ -2734,6 +2767,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)
>> @@ -4172,12 +4219,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;
> 
> Does your device support event delivery in case of the status change?
> ice and mlx5 drivers do poll for changes in this area anyway. It's a
> part of this patchset. You should do the same if your device does
> not support events.
> 
> Could you please implement notifications using
> dpll_device_notify() for status change and dpll_pin_notify() for pin
> state change?
> 

We are working on implementation of interrupt-based notifications, 
that's why I didn't implement polling code. Hopefully it will be ready 
before the non-RFC patchset submission.

> 
>> +
>> +	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_SOURCE :
>> +				  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)
> 
> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
> is not set and therefore the check in dpll_pin_direction_set() will be
> true and -EOPNOTSUPP will be returned from there.
> Remove this.

Yep, will do it.

> 
>> +		return -EOPNOTSUPP;
>> +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> +			    SMA_MODE_IN : SMA_MODE_OUT;
>> +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
> 
> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
> tne sma_nr completely?
>

I wanted to add minimal changes to the driver, I will consider changing 
this as a separate net-next patch.

> 
>> +}
>> +
>> +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)
> 
> In that case, just fill up a single frequency in the properties,
> avoid this check-fail and let the dpll core handle it.
> 

Makes sense.

> 
>> +		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);
> 
> 1) void "()"s here.
> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>     this easier to follow? IDK, just a suggestion, take or leave.
> 
> Same applies to the the rest of similar occurances above.
>

Will do it, yes.

> 
>> +	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,
> 
> This op is a leftover, in dpll core it is not called. This was removed
> and agreed that drivers should implement state_on_dpll_get() op for pins
> to see which one is connected.
> 
> Please fix here and remove the leftover from DPLL patch #2 as well.
> 
> 
>> +	.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) {
>> @@ -4227,8 +4410,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);
> 
> I suggested this the last time, but again: Could you please:
> 1) rename dpll_device_get to __dpll_device_get
> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
> 
> Then drivers will just call always:
> bp->dpll = dpll_device_get(clkid, 0);
> and the macro will fillup the module automatically.
> 
> Please do the same for dpll_pin_get()
> 

Not sure why I have missed this part, but sure will do it.

> 
>> +	if (IS_ERR(bp->dpll)) {
>> +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
> 
> You need to fix your error path to call devlink_unregister() in this
> case.
> 
> 
>> +		goto out;
>> +	}
>> +
>> +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> +	if (err)
> 
> You need to fix your error path to call dpll_device_put() in this
> case.
> 

Ok, I'll re-check and update the error path for the next version.

> 
>> +		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], NULL);
>> +		if (err) {
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +			goto out_dpll;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +out_dpll:
>> +	while (i) {
>> +		--i;
> 
> 	while (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:
>> @@ -4243,7 +4457,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) {
> 
> Remove this pointless check. It is always true.

Agree.

> 
> 
>> +			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.34.1
>>


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
@ 2023-05-05 13:43       ` Vadim Fedorenko
  0 siblings, 0 replies; 149+ messages in thread
From: Vadim Fedorenko @ 2023-05-05 13:43 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

On 04/05/2023 10:27, Jiri Pirko wrote:
> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> Implement basic DPLL operations in ptp_ocp driver as the
>> simplest example of using new subsystem.
>>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> ---
>> drivers/ptp/Kconfig   |   1 +
>> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>> @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> 	SMA_MODE_OUT,
>> };
>>
>> +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
> 
> const

Forgot about this one, will change it.

> 
>> +	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 {
>> @@ -295,6 +305,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;
>> @@ -351,8 +362,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)
>> @@ -836,6 +848,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[] = {
>> @@ -856,31 +869,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 },
>> @@ -891,15 +904,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 },
>> 	{ }
>> };
>>
>> @@ -2283,22 +2296,34 @@ 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 = {
> 
> Why don't you have this as static const outside the function?
> 

Because I'm changing label string for every pin. I cannot change it in
the const object.

> 
>> +		.label = NULL,
> 
> Pointless init.

Agree

>> +		.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.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;
>> 	}
>> @@ -2308,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);
>> @@ -2330,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;
>> @@ -2397,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);
>> }
>> @@ -2446,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 = {
>> +		.label = NULL,
>> +		.type = DPLL_PIN_TYPE_EXT,
>> +		.capabilities = 0,
> 
> Same comment as to the similar prop struct above. Plus another pointless
> init here.
> 

Will remove pointless init.

> 
>> +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> +		.freq_supported = ptp_ocp_sma_freq,
>> +
>> +	};
>> 	u32 reg;
>> 	int i;
>>
>> @@ -2460,16 +2493,16 @@ 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.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) {
>> @@ -2480,9 +2513,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;
>> 		}
>> 	}
>> @@ -2549,6 +2586,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);
>> @@ -2690,16 +2730,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;
>> @@ -2734,6 +2767,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)
>> @@ -4172,12 +4219,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;
> 
> Does your device support event delivery in case of the status change?
> ice and mlx5 drivers do poll for changes in this area anyway. It's a
> part of this patchset. You should do the same if your device does
> not support events.
> 
> Could you please implement notifications using
> dpll_device_notify() for status change and dpll_pin_notify() for pin
> state change?
> 

We are working on implementation of interrupt-based notifications, 
that's why I didn't implement polling code. Hopefully it will be ready 
before the non-RFC patchset submission.

> 
>> +
>> +	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_SOURCE :
>> +				  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)
> 
> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
> is not set and therefore the check in dpll_pin_direction_set() will be
> true and -EOPNOTSUPP will be returned from there.
> Remove this.

Yep, will do it.

> 
>> +		return -EOPNOTSUPP;
>> +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> +			    SMA_MODE_IN : SMA_MODE_OUT;
>> +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
> 
> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
> tne sma_nr completely?
>

I wanted to add minimal changes to the driver, I will consider changing 
this as a separate net-next patch.

> 
>> +}
>> +
>> +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)
> 
> In that case, just fill up a single frequency in the properties,
> avoid this check-fail and let the dpll core handle it.
> 

Makes sense.

> 
>> +		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);
> 
> 1) void "()"s here.
> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>     this easier to follow? IDK, just a suggestion, take or leave.
> 
> Same applies to the the rest of similar occurances above.
>

Will do it, yes.

> 
>> +	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,
> 
> This op is a leftover, in dpll core it is not called. This was removed
> and agreed that drivers should implement state_on_dpll_get() op for pins
> to see which one is connected.
> 
> Please fix here and remove the leftover from DPLL patch #2 as well.
> 
> 
>> +	.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) {
>> @@ -4227,8 +4410,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);
> 
> I suggested this the last time, but again: Could you please:
> 1) rename dpll_device_get to __dpll_device_get
> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
> 
> Then drivers will just call always:
> bp->dpll = dpll_device_get(clkid, 0);
> and the macro will fillup the module automatically.
> 
> Please do the same for dpll_pin_get()
> 

Not sure why I have missed this part, but sure will do it.

> 
>> +	if (IS_ERR(bp->dpll)) {
>> +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
> 
> You need to fix your error path to call devlink_unregister() in this
> case.
> 
> 
>> +		goto out;
>> +	}
>> +
>> +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> +	if (err)
> 
> You need to fix your error path to call dpll_device_put() in this
> case.
> 

Ok, I'll re-check and update the error path for the next version.

> 
>> +		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], NULL);
>> +		if (err) {
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +			goto out_dpll;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +out_dpll:
>> +	while (i) {
>> +		--i;
> 
> 	while (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:
>> @@ -4243,7 +4457,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) {
> 
> Remove this pointless check. It is always true.

Agree.

> 
> 
>> +			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.34.1
>>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-05-05 13:16       ` Vadim Fedorenko
@ 2023-05-05 15:29         ` Jakub Kicinski
  -1 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-05 15:29 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jiri Pirko, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, Milena Olech, Michal Michalik,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk

On Fri, 5 May 2023 14:16:53 +0100 Vadim Fedorenko wrote:
> >> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),  
> > 
> > Why is direction set on a pin? We can't chain DPLLs?  
> 
> We can chain DPLLs using pins only. We don't have any interface to
> configure 2 pins to connect 2 different DPLLs to each other at the same 
> time. The configuration should take care of one pin being input and
> other one being output. That's why we have direction property attached
> to the pin, not the DPLL itself.

Makes sense.

> >> +Device driver implementation
> >> +============================
> >> +
> >> +Device is allocated by ``dpll_device_get`` call. Second call with the
> >> +same arguments doesn't create new object but provides pointer to
> >> +previously created device for given arguments, it also increase 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.  
> > 
> > You can add () after the function name and render the kdoc at the end
> > of this doc. The `` marking will then be unnecessary.
> >   
> Mmm... any examples of such a way of creating documentation? I was
> following tls*.rst style, but without copying code-blocks.

net_dim.rst, maybe?  driver.rst ? Feel free to ping me 1:1 if you're
struggling, it should be fairly straightforward.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
@ 2023-05-05 15:29         ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-05 15:29 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jiri Pirko, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, Milena Olech, Michal Michalik,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk

On Fri, 5 May 2023 14:16:53 +0100 Vadim Fedorenko wrote:
> >> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),  
> > 
> > Why is direction set on a pin? We can't chain DPLLs?  
> 
> We can chain DPLLs using pins only. We don't have any interface to
> configure 2 pins to connect 2 different DPLLs to each other at the same 
> time. The configuration should take care of one pin being input and
> other one being output. That's why we have direction property attached
> to the pin, not the DPLL itself.

Makes sense.

> >> +Device driver implementation
> >> +============================
> >> +
> >> +Device is allocated by ``dpll_device_get`` call. Second call with the
> >> +same arguments doesn't create new object but provides pointer to
> >> +previously created device for given arguments, it also increase 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.  
> > 
> > You can add () after the function name and render the kdoc at the end
> > of this doc. The `` marking will then be unnecessary.
> >   
> Mmm... any examples of such a way of creating documentation? I was
> following tls*.rst style, but without copying code-blocks.

net_dim.rst, maybe?  driver.rst ? Feel free to ping me 1:1 if you're
struggling, it should be fairly straightforward.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
  2023-05-05 13:43       ` Vadim Fedorenko
@ 2023-05-06 12:42         ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-06 12:42 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, Milena Olech, Michal Michalik,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk

Fri, May 05, 2023 at 03:43:08PM CEST, vadim.fedorenko@linux.dev wrote:
>On 04/05/2023 10:27, Jiri Pirko wrote:
>> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> > From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > 
>> > Implement basic DPLL operations in ptp_ocp driver as the
>> > simplest example of using new subsystem.
>> > 
>> > Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > ---
>> > drivers/ptp/Kconfig   |   1 +
>> > drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> > 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>> > @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> > 	SMA_MODE_OUT,
>> > };
>> > 
>> > +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
>> 
>> const
>
>Forgot about this one, will change it.
>
>> 
>> > +	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 {
>> > @@ -295,6 +305,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;
>> > @@ -351,8 +362,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)
>> > @@ -836,6 +848,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[] = {
>> > @@ -856,31 +869,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 },
>> > @@ -891,15 +904,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 },
>> > 	{ }
>> > };
>> > 
>> > @@ -2283,22 +2296,34 @@ 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 = {
>> 
>> Why don't you have this as static const outside the function?
>> 
>
>Because I'm changing label string for every pin. I cannot change it in
>the const object.

No you don't. You just copy prop to bp->sma[i].dpll_prop. Prop does not
change.


>
>> 
>> > +		.label = NULL,
>> 
>> Pointless init.
>
>Agree
>
>> > +		.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.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;
>> > 	}
>> > @@ -2308,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);
>> > @@ -2330,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;
>> > @@ -2397,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);
>> > }
>> > @@ -2446,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 = {
>> > +		.label = NULL,
>> > +		.type = DPLL_PIN_TYPE_EXT,
>> > +		.capabilities = 0,
>> 
>> Same comment as to the similar prop struct above. Plus another pointless
>> init here.
>> 
>
>Will remove pointless init.
>
>> 
>> > +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> > +		.freq_supported = ptp_ocp_sma_freq,
>> > +
>> > +	};
>> > 	u32 reg;
>> > 	int i;
>> > 
>> > @@ -2460,16 +2493,16 @@ 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.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) {
>> > @@ -2480,9 +2513,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;
>> > 		}
>> > 	}
>> > @@ -2549,6 +2586,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);
>> > @@ -2690,16 +2730,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;
>> > @@ -2734,6 +2767,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)
>> > @@ -4172,12 +4219,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;
>> 
>> Does your device support event delivery in case of the status change?
>> ice and mlx5 drivers do poll for changes in this area anyway. It's a
>> part of this patchset. You should do the same if your device does
>> not support events.
>> 
>> Could you please implement notifications using
>> dpll_device_notify() for status change and dpll_pin_notify() for pin
>> state change?
>> 
>
>We are working on implementation of interrupt-based notifications, that's why
>I didn't implement polling code. Hopefully it will be ready before the
>non-RFC patchset submission.

Do polling implementation if not and replace by interrupt driver later.
Point is, there have to be notifications from day 1.


>
>> 
>> > +
>> > +	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_SOURCE :
>> > +				  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)
>> 
>> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
>> is not set and therefore the check in dpll_pin_direction_set() will be
>> true and -EOPNOTSUPP will be returned from there.
>> Remove this.
>
>Yep, will do it.
>
>> 
>> > +		return -EOPNOTSUPP;
>> > +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> > +			    SMA_MODE_IN : SMA_MODE_OUT;
>> > +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
>> 
>> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
>> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
>> tne sma_nr completely?
>> 
>
>I wanted to add minimal changes to the driver, I will consider changing this
>as a separate net-next patch.

Hmm, you do change ptp_ocp_sma_store_val() here anyway. Could be an
extra patch in this patchset before this one if you want to have it
separate.


>
>> 
>> > +}
>> > +
>> > +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)
>> 
>> In that case, just fill up a single frequency in the properties,
>> avoid this check-fail and let the dpll core handle it.
>> 
>
>Makes sense.
>
>> 
>> > +		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);
>> 
>> 1) void "()"s here.
>> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>>     this easier to follow? IDK, just a suggestion, take or leave.
>> 
>> Same applies to the the rest of similar occurances above.
>> 
>
>Will do it, yes.
>
>> 
>> > +	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,
>> 
>> This op is a leftover, in dpll core it is not called. This was removed
>> and agreed that drivers should implement state_on_dpll_get() op for pins
>> to see which one is connected.
>> 
>> Please fix here and remove the leftover from DPLL patch #2 as well.

Did you miss this by any chance?


>> 
>> 
>> > +	.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) {
>> > @@ -4227,8 +4410,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);
>> 
>> I suggested this the last time, but again: Could you please:
>> 1) rename dpll_device_get to __dpll_device_get
>> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
>> 
>> Then drivers will just call always:
>> bp->dpll = dpll_device_get(clkid, 0);
>> and the macro will fillup the module automatically.
>> 
>> Please do the same for dpll_pin_get()
>> 
>
>Not sure why I have missed this part, but sure will do it.

Awesome.


>
>> 
>> > +	if (IS_ERR(bp->dpll)) {
>> > +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>> 
>> You need to fix your error path to call devlink_unregister() in this
>> case.
>> 
>> 
>> > +		goto out;
>> > +	}
>> > +
>> > +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> > +	if (err)
>> 
>> You need to fix your error path to call dpll_device_put() in this
>> case.
>> 
>
>Ok, I'll re-check and update the error path for the next version.
>
>> 
>> > +		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], NULL);
>> > +		if (err) {
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +			goto out_dpll;
>> > +		}
>> > +	}
>> > +
>> > +	return 0;
>> > +out_dpll:
>> > +	while (i) {
>> > +		--i;
>> 
>> 	while (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:
>> > @@ -4243,7 +4457,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) {
>> 
>> Remove this pointless check. It is always true.
>
>Agree.
>
>> 
>> 
>> > +			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.34.1
>> > 
>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops
@ 2023-05-06 12:42         ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-06 12:42 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, Milena Olech, Michal Michalik,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk

Fri, May 05, 2023 at 03:43:08PM CEST, vadim.fedorenko@linux.dev wrote:
>On 04/05/2023 10:27, Jiri Pirko wrote:
>> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> > From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > 
>> > Implement basic DPLL operations in ptp_ocp driver as the
>> > simplest example of using new subsystem.
>> > 
>> > Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > ---
>> > drivers/ptp/Kconfig   |   1 +
>> > drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> > 2 files changed, 276 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 2b63f3487645..100e5da0aeb3 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
>> > @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> > 	SMA_MODE_OUT,
>> > };
>> > 
>> > +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
>> 
>> const
>
>Forgot about this one, will change it.
>
>> 
>> > +	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 {
>> > @@ -295,6 +305,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;
>> > @@ -351,8 +362,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)
>> > @@ -836,6 +848,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[] = {
>> > @@ -856,31 +869,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 },
>> > @@ -891,15 +904,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 },
>> > 	{ }
>> > };
>> > 
>> > @@ -2283,22 +2296,34 @@ 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 = {
>> 
>> Why don't you have this as static const outside the function?
>> 
>
>Because I'm changing label string for every pin. I cannot change it in
>the const object.

No you don't. You just copy prop to bp->sma[i].dpll_prop. Prop does not
change.


>
>> 
>> > +		.label = NULL,
>> 
>> Pointless init.
>
>Agree
>
>> > +		.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.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;
>> > 	}
>> > @@ -2308,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);
>> > @@ -2330,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;
>> > @@ -2397,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);
>> > }
>> > @@ -2446,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 = {
>> > +		.label = NULL,
>> > +		.type = DPLL_PIN_TYPE_EXT,
>> > +		.capabilities = 0,
>> 
>> Same comment as to the similar prop struct above. Plus another pointless
>> init here.
>> 
>
>Will remove pointless init.
>
>> 
>> > +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> > +		.freq_supported = ptp_ocp_sma_freq,
>> > +
>> > +	};
>> > 	u32 reg;
>> > 	int i;
>> > 
>> > @@ -2460,16 +2493,16 @@ 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.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) {
>> > @@ -2480,9 +2513,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;
>> > 		}
>> > 	}
>> > @@ -2549,6 +2586,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);
>> > @@ -2690,16 +2730,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;
>> > @@ -2734,6 +2767,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)
>> > @@ -4172,12 +4219,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;
>> 
>> Does your device support event delivery in case of the status change?
>> ice and mlx5 drivers do poll for changes in this area anyway. It's a
>> part of this patchset. You should do the same if your device does
>> not support events.
>> 
>> Could you please implement notifications using
>> dpll_device_notify() for status change and dpll_pin_notify() for pin
>> state change?
>> 
>
>We are working on implementation of interrupt-based notifications, that's why
>I didn't implement polling code. Hopefully it will be ready before the
>non-RFC patchset submission.

Do polling implementation if not and replace by interrupt driver later.
Point is, there have to be notifications from day 1.


>
>> 
>> > +
>> > +	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_SOURCE :
>> > +				  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)
>> 
>> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
>> is not set and therefore the check in dpll_pin_direction_set() will be
>> true and -EOPNOTSUPP will be returned from there.
>> Remove this.
>
>Yep, will do it.
>
>> 
>> > +		return -EOPNOTSUPP;
>> > +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> > +			    SMA_MODE_IN : SMA_MODE_OUT;
>> > +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
>> 
>> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
>> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
>> tne sma_nr completely?
>> 
>
>I wanted to add minimal changes to the driver, I will consider changing this
>as a separate net-next patch.

Hmm, you do change ptp_ocp_sma_store_val() here anyway. Could be an
extra patch in this patchset before this one if you want to have it
separate.


>
>> 
>> > +}
>> > +
>> > +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)
>> 
>> In that case, just fill up a single frequency in the properties,
>> avoid this check-fail and let the dpll core handle it.
>> 
>
>Makes sense.
>
>> 
>> > +		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);
>> 
>> 1) void "()"s here.
>> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>>     this easier to follow? IDK, just a suggestion, take or leave.
>> 
>> Same applies to the the rest of similar occurances above.
>> 
>
>Will do it, yes.
>
>> 
>> > +	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,
>> 
>> This op is a leftover, in dpll core it is not called. This was removed
>> and agreed that drivers should implement state_on_dpll_get() op for pins
>> to see which one is connected.
>> 
>> Please fix here and remove the leftover from DPLL patch #2 as well.

Did you miss this by any chance?


>> 
>> 
>> > +	.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) {
>> > @@ -4227,8 +4410,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);
>> 
>> I suggested this the last time, but again: Could you please:
>> 1) rename dpll_device_get to __dpll_device_get
>> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
>> 
>> Then drivers will just call always:
>> bp->dpll = dpll_device_get(clkid, 0);
>> and the macro will fillup the module automatically.
>> 
>> Please do the same for dpll_pin_get()
>> 
>
>Not sure why I have missed this part, but sure will do it.

Awesome.


>
>> 
>> > +	if (IS_ERR(bp->dpll)) {
>> > +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>> 
>> You need to fix your error path to call devlink_unregister() in this
>> case.
>> 
>> 
>> > +		goto out;
>> > +	}
>> > +
>> > +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> > +	if (err)
>> 
>> You need to fix your error path to call dpll_device_put() in this
>> case.
>> 
>
>Ok, I'll re-check and update the error path for the next version.
>
>> 
>> > +		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], NULL);
>> > +		if (err) {
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +			goto out_dpll;
>> > +		}
>> > +	}
>> > +
>> > +	return 0;
>> > +out_dpll:
>> > +	while (i) {
>> > +		--i;
>> 
>> 	while (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:
>> > @@ -4243,7 +4457,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) {
>> 
>> Remove this pointless check. It is always true.
>
>Agree.
>
>> 
>> 
>> > +			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.34.1
>> > 
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
                     ` (4 preceding siblings ...)
  (?)
@ 2023-05-09 13:40   ` Jiri Pirko
  2023-06-09 12:53       ` Kubalewski, Arkadiusz
  -1 siblings, 1 reply; 149+ messages in thread
From: Jiri Pirko @ 2023-05-09 13:40 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>

[...]

>+int dpll_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_xa_lock);

Did you test this?

I'm gettting following deadlock warning:

[  280.899789] ======================================================
[  280.900458] WARNING: possible circular locking dependency detected
[  280.901126] 6.3.0jiri+ #4 Tainted: G             L    
[  280.901702] ------------------------------------------------------
[  280.902378] python3/1058 is trying to acquire lock:
[  280.902934] ffff88811571ae88 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}, at: netlink_dump+0x4a/0x400
[  280.903869] 
               but task is already holding lock:
[  280.904559] ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at: dpll_pin_pre_dumpit+0x13/0x20
[  280.905464] 
               which lock already depends on the new lock.

[  280.906414] 
               the existing dependency chain (in reverse order) is:
[  280.907141] 
               -> #1 (dpll_xa_lock){+.+.}-{3:3}:
[  280.907711]        __mutex_lock+0x91/0xbb0
[  280.908116]        dpll_pin_pre_dumpit+0x13/0x20
[  280.908553]        genl_start+0xc6/0x150
[  280.908940]        __netlink_dump_start+0x158/0x230
[  280.909399]        genl_family_rcv_msg_dumpit+0xf9/0x110
[  280.909894]        genl_rcv_msg+0x115/0x290
[  280.910302]        netlink_rcv_skb+0x54/0x100
[  280.910726]        genl_rcv+0x24/0x40
[  280.911106]        netlink_unicast+0x182/0x260
[  280.911547]        netlink_sendmsg+0x242/0x4b0
[  280.911984]        sock_sendmsg+0x38/0x60
[  280.912384]        __sys_sendto+0xeb/0x130
[  280.912797]        __x64_sys_sendto+0x20/0x30
[  280.913227]        do_syscall_64+0x3c/0x80
[  280.913639]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
[  280.914156] 
               -> #0 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}:
[  280.914809]        __lock_acquire+0x1165/0x26b0
[  280.915254]        lock_acquire+0xce/0x2b0
[  280.915665]        __mutex_lock+0x91/0xbb0
[  280.916080]        netlink_dump+0x4a/0x400
[  280.916488]        __netlink_dump_start+0x188/0x230
[  280.916953]        genl_family_rcv_msg_dumpit+0xf9/0x110
[  280.917448]        genl_rcv_msg+0x115/0x290
[  280.917863]        netlink_rcv_skb+0x54/0x100
[  280.918301]        genl_rcv+0x24/0x40
[  280.918686]        netlink_unicast+0x182/0x260
[  280.919129]        netlink_sendmsg+0x242/0x4b0
[  280.919569]        sock_sendmsg+0x38/0x60
[  280.919969]        __sys_sendto+0xeb/0x130
[  280.920377]        __x64_sys_sendto+0x20/0x30
[  280.920808]        do_syscall_64+0x3c/0x80
[  280.921220]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
[  280.921730] 
               other info that might help us debug this:

[  280.922513]  Possible unsafe locking scenario:

[  280.923095]        CPU0                    CPU1
[  280.923541]        ----                    ----
[  280.923976]   lock(dpll_xa_lock);
[  280.924329]                                lock(nlk_cb_mutex-GENERIC);
[  280.924916]                                lock(dpll_xa_lock);
[  280.925454]   lock(nlk_cb_mutex-GENERIC);
[  280.925858] 
                *** DEADLOCK ***

[  280.926488] 2 locks held by python3/1058:
[  280.926891]  #0: ffffffff827e2430 (cb_lock){++++}-{3:3}, at: genl_rcv+0x15/0x40
[  280.927585]  #1: ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at: dpll_pin_pre_dumpit+0x13/0x20
[  280.928385] 
               stack backtrace:
[  280.928853] CPU: 8 PID: 1058 Comm: python3 Tainted: G             L     6.3.0jiri+ #4
[  280.929586] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
[  280.930558] Call Trace:
[  280.930849]  <TASK>
[  280.931117]  dump_stack_lvl+0x58/0xb0
[  280.931500]  check_noncircular+0x11b/0x130
[  280.931916]  ? kernel_text_address+0x109/0x110
[  280.932353]  __lock_acquire+0x1165/0x26b0
[  280.932759]  lock_acquire+0xce/0x2b0
[  280.933130]  ? netlink_dump+0x4a/0x400
[  280.933517]  __mutex_lock+0x91/0xbb0
[  280.933885]  ? netlink_dump+0x4a/0x400
[  280.934269]  ? netlink_dump+0x4a/0x400
[  280.934662]  ? netlink_dump+0x4a/0x400
[  280.935054]  netlink_dump+0x4a/0x400
[  280.935426]  __netlink_dump_start+0x188/0x230
[  280.935857]  genl_family_rcv_msg_dumpit+0xf9/0x110
[  280.936321]  ? genl_family_rcv_msg_attrs_parse.constprop.0+0xe0/0xe0
[  280.936887]  ? dpll_nl_pin_get_doit+0x100/0x100
[  280.937324]  ? genl_lock_dumpit+0x50/0x50
[  280.937729]  genl_rcv_msg+0x115/0x290
[  280.938109]  ? dpll_pin_post_doit+0x20/0x20
[  280.938526]  ? dpll_nl_pin_get_doit+0x100/0x100
[  280.938966]  ? dpll_pin_pre_dumpit+0x20/0x20
[  280.939390]  ? genl_family_rcv_msg_doit.isra.0+0x110/0x110
[  280.939904]  netlink_rcv_skb+0x54/0x100
[  280.940296]  genl_rcv+0x24/0x40
[  280.940636]  netlink_unicast+0x182/0x260
[  280.941034]  netlink_sendmsg+0x242/0x4b0
[  280.941439]  sock_sendmsg+0x38/0x60
[  280.941804]  ? sockfd_lookup_light+0x12/0x70
[  280.942230]  __sys_sendto+0xeb/0x130
[  280.942616]  ? mntput_no_expire+0x7e/0x490
[  280.943038]  ? proc_nr_files+0x30/0x30
[  280.943425]  __x64_sys_sendto+0x20/0x30
[  280.943817]  do_syscall_64+0x3c/0x80
[  280.944194]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
[  280.944674] RIP: 0033:0x7f252fd132b0
[  280.945042] Code: c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 1d 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 68 c3 0f 1f 80 00 00 00 00 41 54 48 83 ec 20
[  280.946622] RSP: 002b:00007ffdbd9335d8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
[  280.947328] RAX: ffffffffffffffda RBX: 00007ffdbd933688 RCX: 00007f252fd132b0
[  280.947962] RDX: 0000000000000014 RSI: 00007f252ede65d0 RDI: 0000000000000003
[  280.948594] RBP: 00007f252f806da0 R08: 0000000000000000 R09: 0000000000000000
[  280.949229] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
[  280.949858] R13: ffffffffc4653600 R14: 0000000000000001 R15: 00007f252f74d147
[  280.950494]  </TASK>

Problem is that in __netlink_dump_start() you take dpll_xa_lock
(in control->start(cb)) while holding nlk->cb_mutex, then you unlock
the nlk->cb_mutex and take it again in netlink_dump().
I hear "Chiquitita" from the distance :)

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-04 12:02     ` Jiri Pirko
  (?)
  (?)
@ 2023-05-11  7:38     ` Kubalewski, Arkadiusz
  2023-05-11  8:14       ` Jiri Pirko
  2023-05-11 15:26       ` Jakub Kicinski
  -1 siblings, 2 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11  7:38 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Vadim Fedorenko

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, May 4, 2023 2:03 PM
>
>Fri, Apr 28, 2023 at 02:20:02AM CEST, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>Add a protocol spec for DPLL.
>>Add code generated from the spec.
>>
>>Signed-off-by: Jakub Kicinski <kuba@kernel.org>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>---
>> Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
>> drivers/dpll/dpll_nl.c                | 126 +++++++
>> drivers/dpll/dpll_nl.h                |  42 +++
>> include/uapi/linux/dpll.h             | 202 +++++++++++
>> 4 files changed, 842 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..67ca0f6cf2d5
>>--- /dev/null
>>+++ b/Documentation/netlink/specs/dpll.yaml
>>@@ -0,0 +1,472 @@
>>+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
>>+      attribute
>>+    entries:
>>+      -
>>+        name: unspec
>
>In general, why exactly do we need unspec values in enums and CMDs?
>What is the usecase. If there isn't please remove.
>

Sure, fixed.

>
>>+        doc: unspecified value
>>+      -
>>+        name: manual
>>+        doc: source can be only selected by sending a request to dpll
>>+      -
>>+        name: automatic
>>+        doc: highest prio, valid source, auto selected by dpll
>>+      -
>>+        name: holdover
>>+        doc: dpll forced into holdover mode
>>+      -
>>+        name: freerun
>>+        doc: dpll driven on system clk, no holdover available
>
>Remove "no holdover available". This is not a state, this is a mode
>configuration. If holdover is or isn't available, is a runtime info.
>

Fiexd.

>
>>+      -
>>+        name: nco
>>+        doc: dpll driven by Numerically Controlled Oscillator
>>+    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: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: unlocked
>>+        doc: |
>>+          dpll was not yet locked to any valid source (or is in one of
>>+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>+      -
>>+        name: calibrating
>>+        doc: dpll is trying to lock to a valid signal
>>+      -
>>+        name: locked
>>+        doc: dpll is locked
>>+      -
>>+        name: holdover
>>+        doc: |
>>+          dpll is in holdover state - lost a valid lock or was forced by
>>+          selecting DPLL_MODE_HOLDOVER mode
>
>Is it needed to mention the holdover mode. It's slightly confusing,
>because user might understand that the lock-status is always "holdover"
>in case of "holdover" mode. But it could be "unlocked", can't it?
>Perhaps I don't understand the flows there correctly :/
>

Yes, it could be unlocked even when user requests the 'holdover' mode, i.e.
when the dpll was not locked to a valid source before requesting the mode.
Improved the docs:
        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 it was not, the dpll's lock-status will remain
          DPLL_LOCK_STATUS_UNLOCKED even if user requests
          DPLL_MODE_HOLDOVER)
Is that better?

>
>>+    render-max: true
>>+  -
>>+    type: const
>>+    name: temp-divider
>>+    value: 10
>>+    doc: |
>>+      temperature divider allowing userspace to calculate the
>>+      temperature as float with single digit precision.
>>+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>>+      tempearture value.
>
>s/tempearture/temperature/
>
>Didn't checkpatch warn you?
>

Fixed, thanks!
No, I don't think it did.

>
>>+      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: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: pps
>>+        doc: dpll produces Pulse-Per-Second signal
>>+      -
>>+        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: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: mux
>>+        doc: aggregates another layer of selectable pins
>>+      -
>>+        name: ext
>>+        doc: external source
>>+      -
>>+        name: synce-eth-port
>>+        doc: ethernet port PHY's recovered clock
>>+      -
>>+        name: int-oscillator
>>+        doc: device internal oscillator
>
>Is this somehow related to the mode "nco" (Numerically Controlled
>Oscillator)?
>

Yes.

>
>
>>+      -
>>+        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: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: source
>>+        doc: pin used as a source of a signal
>>+      -
>>+        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-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: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: connected
>>+        doc: pin connected, active source of phase locked loop
>>+      -
>>+        name: disconnected
>>+        doc: pin disconnected, not considered as a valid source
>>+      -
>>+        name: selectable
>>+        doc: pin enabled for automatic source 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
>>+  -
>>+    type: enum
>>+    name: event
>>+    doc: events of dpll generic netlink family
>>+    entries:
>>+      -
>>+        name: unspec
>>+        doc: invalid event type
>>+      -
>>+        name: device-create
>>+        doc: dpll device created
>>+      -
>>+        name: device-delete
>>+        doc: dpll device deleted
>>+      -
>>+        name: device-change
>
>Please have a separate create/delete/change values for pins.
>

Makes sense, but details, pin creation doesn't occur from uAPI perspective,
as the pins itself are not visible to the user. They are visible after they
are registered with a device, thus we would have to do something like:
- pin-register
- pin-unregister
- pin-change

Does it make sense?

>
>>+        doc: |
>>+          attribute of dpll device or pin changed, reason is to be found
>>with
>>+          an attribute type (DPLL_A_*) received with the event
>>+
>>+
>>+attribute-sets:
>>+  -
>>+    name: dpll
>>+    enum-name: dplla
>>+    attributes:
>>+      -
>>+        name: device
>>+        type: nest
>>+        value: 1
>
>Why not 0?
>

Sorry I don't recall what exact technical reasons are behind it, but all
netlink attributes I have found have 0 value attribute unused/unspec.

>Also, Plese don't have this attr as a first one. It is related to
>PIN_GET/SET cmd, it should be somewhere among related attributes.
>
>Definitelly, the handle ATTR/ATTTs should be the first one/ones.
>

Sure, fixed.

>
>
>>+        multi-attr: true
>>+        nested-attributes: device
>>+      -
>>+        name: id
>>+        type: u32
>>+      -
>>+        name: dev-name
>>+        type: string
>>+      -
>>+        name: bus-name
>>+        type: string
>>+      -
>>+        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: clock-id
>>+        type: u64
>>+      -
>>+        name: type
>>+        type: u8
>>+        enum: type
>>+      -
>>+        name: pin-idx
>>+        type: u32
>>+      -
>>+        name: pin-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-parent
>>+        type: nest
>>+        multi-attr: true
>>+        nested-attributes: pin-parent
>>+      -
>>+        name: pin-parent-idx
>>+        type: u32
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>>+      -
>>+        name: pin-dpll-caps
>>+        type: u32
>>+  -
>>+    name: device
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: id
>>+        type: u32
>>+        value: 2
>>+      -
>>+        name: dev-name
>>+        type: string
>>+      -
>>+        name: bus-name
>>+        type: string
>>+      -
>>+        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: clock-id
>>+        type: u64
>>+      -
>>+        name: type
>>+        type: u8
>>+        enum: type
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+        value: 19
>
>Do you still need to pass values for a subset? That is odd. Well, I
>think is is odd to pass anything other than names in subset definition,
>the rest of the info is in the original attribute set definition,
>isn't it?
>Jakub?
>

Yes it is fixed, I will remove those.

>
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+  -
>>+    name: pin-parent
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        value: 20
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent-idx
>>+        type: u32
>>+        value: 22
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>>+  -
>>+    name: pin-frequency-range
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: pin-frequency-min
>>+        type: u64
>>+        value: 17
>>+      -
>>+        name: pin-frequency-max
>>+        type: u64
>>+
>>+operations:
>>+  list:
>>+    -
>>+      name: unspec
>>+      doc: unused
>>+
>>+    -
>>+      name: device-get
>>+      doc: |
>>+        Get list of DPLL devices (dump) or attributes of a single dpll
>>device
>>+      attribute-set: dpll
>>+      flags: [ admin-perm ]
>
>I may be missing something, but why do you enforce adming perm for
>get/dump cmds?
>

Yes, security reasons, we don't want regular users to spam-query the driver
ops. Also explained in docs:
All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
any spamming/D.o.S. from unauthorized userspace applications.

>
>>+
>>+      do:
>>+        pre: dpll-pre-doit
>>+        post: dpll-post-doit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+        reply:
>>+          attributes:
>>+            - device
>>+
>>+      dump:
>>+        pre: dpll-pre-dumpit
>>+        post: dpll-post-dumpit
>>+        reply:
>>+          attributes:
>>+            - device
>
>I might be missing something, but this means "device" netdev attribute
>DPLL_A_DEVICE, right? If yes, that is incorrect and you should list all
>the device attrs.
>

Actually this means that attributes expected in response to this command are
from `device` subset.
But I see your point, will make `device` subset only for pin's nested
attributes, and here will list device attributes.

>
>>+
>>+    -
>>+      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
>>+            - bus-name
>>+            - dev-name
>>+            - mode
>>+
>>+    -
>>+      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
>>+            - bus-name
>>+            - dev-name
>>+            - pin-idx
>>+        reply: &pin-attrs
>>+          attributes:
>>+            - pin-idx
>>+            - pin-label
>>+            - pin-type
>>+            - pin-direction
>>+            - pin-frequency
>>+            - pin-frequency-supported
>>+            - pin-parent
>>+            - pin-rclk-device
>>+            - pin-dpll-caps
>>+            - device
>>+
>>+      dump:
>>+        pre: dpll-pin-pre-dumpit
>>+        post: dpll-pin-post-dumpit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+        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
>>+            - bus-name
>>+            - dev-name
>>+            - pin-idx
>>+            - pin-frequency
>>+            - pin-direction
>>+            - pin-prio
>>+            - pin-state
>>+            - pin-parent-idx
>>+
>>+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..2f8643f401b0
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.c
>>@@ -0,0 +1,126 @@
>>+// 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 <linux/dpll.h>
>>+
>>+/* DPLL_CMD_DEVICE_GET - do */
>>+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME
>>+ 1] = {
>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_MODE] = NLA_POLICY_MAX(NLA_U8, 5),
>
>I know it is a matter of the generator script, still have to note it
>hurts my eyes to see "5" here :)
>

Yes, that's true.

Thanks!
Arkadiusz

>
>>+};
>>+
>>+/* DPLL_CMD_PIN_GET - do */
>>+static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_IDX +
>>1] = {
>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
>>+};
>>+
>>+/* DPLL_CMD_PIN_GET - dump */
>>+static const struct nla_policy
>>dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>+};
>>+
>>+/* DPLL_CMD_PIN_SET - do */
>>+static const struct nla_policy
>>dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
>>+};
>>+
>>+/* Ops table for dpll */
>>+static const struct genl_split_ops dpll_nl_ops[] = {
>>+	{
>>+		.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_BUS_NAME,
>>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>>+	},
>>+	{
>>+		.cmd	= DPLL_CMD_DEVICE_GET,
>>+		.start	= dpll_pre_dumpit,
>>+		.dumpit	= dpll_nl_device_get_dumpit,
>>+		.done	= dpll_post_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_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_IDX,
>>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>>+	},
>>+	{
>>+		.cmd		= DPLL_CMD_PIN_GET,
>>+		.start		= dpll_pin_pre_dumpit,
>>+		.dumpit		= dpll_nl_pin_get_dumpit,
>>+		.done		= dpll_pin_post_dumpit,
>>+		.policy		= dpll_pin_get_dump_nl_policy,
>>+		.maxattr	= DPLL_A_BUS_NAME,
>>+		.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_IDX,
>>+		.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..57ab2da562ba
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.h
>>@@ -0,0 +1,42 @@
>>+/* 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 <linux/dpll.h>
>>+
>>+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_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_pre_dumpit(struct netlink_callback *cb);
>>+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
>>+int dpll_post_dumpit(struct netlink_callback *cb);
>>+int dpll_pin_post_dumpit(struct netlink_callback *cb);
>>+
>>+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_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..e188bc189754
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,202 @@
>>+/* 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 sources to syntonize with it, valid values for
>>+ *   DPLL_A_MODE attribute
>>+ * @DPLL_MODE_UNSPEC: unspecified value
>>+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to
>>dpll
>>+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
>>+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
>>+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
>>+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
>>+ */
>>+enum dpll_mode {
>>+	DPLL_MODE_UNSPEC,
>>+	DPLL_MODE_MANUAL,
>>+	DPLL_MODE_AUTOMATIC,
>>+	DPLL_MODE_HOLDOVER,
>>+	DPLL_MODE_FREERUN,
>>+	DPLL_MODE_NCO,
>>+
>>+	__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_UNSPEC: unspecified value
>>+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid
>>source (or
>>+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid
>>signal
>>+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
>>+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid
>>lock or
>>+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
>>+ */
>>+enum dpll_lock_status {
>>+	DPLL_LOCK_STATUS_UNSPEC,
>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>+	DPLL_LOCK_STATUS_LOCKED,
>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>+
>>+	__DPLL_LOCK_STATUS_MAX,
>>+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
>>+};
>>+
>>+#define DPLL_TEMP_DIVIDER	10
>>+
>>+/**
>>+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
>>+ * @DPLL_TYPE_UNSPEC: unspecified value
>>+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
>>+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
>>+ */
>>+enum dpll_type {
>>+	DPLL_TYPE_UNSPEC,
>>+	DPLL_TYPE_PPS,
>>+	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_UNSPEC: unspecified value
>>+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
>>+ * @DPLL_PIN_TYPE_EXT: external source
>>+ * @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_UNSPEC,
>>+	DPLL_PIN_TYPE_MUX,
>>+	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_UNSPEC: unspecified value
>>+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
>>+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
>>+ */
>>+enum dpll_pin_direction {
>>+	DPLL_PIN_DIRECTION_UNSPEC,
>>+	DPLL_PIN_DIRECTION_SOURCE,
>>+	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_MHZ	10000000
>>+
>>+/**
>>+ * enum dpll_pin_state - defines possible states of a pin, valid values for
>>+ *   DPLL_A_PIN_STATE attribute
>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase
>locked loop
>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
>>+ *   source
>>+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
>>+ */
>>+enum dpll_pin_state {
>>+	DPLL_PIN_STATE_UNSPEC,
>>+	DPLL_PIN_STATE_CONNECTED,
>>+	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_event - events of dpll generic netlink family
>>+ * @DPLL_EVENT_UNSPEC: invalid event type
>>+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
>>+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
>>+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed,
>>reason
>>+ *   is to be found with an attribute type (DPLL_A_*) received with the
>>event
>>+ */
>>+enum dpll_event {
>>+	DPLL_EVENT_UNSPEC,
>>+	DPLL_EVENT_DEVICE_CREATE,
>>+	DPLL_EVENT_DEVICE_DELETE,
>>+	DPLL_EVENT_DEVICE_CHANGE,
>>+};
>>+
>>+enum dplla {
>>+	DPLL_A_DEVICE = 1,
>>+	DPLL_A_ID,
>>+	DPLL_A_DEV_NAME,
>>+	DPLL_A_BUS_NAME,
>>+	DPLL_A_MODE,
>>+	DPLL_A_MODE_SUPPORTED,
>>+	DPLL_A_LOCK_STATUS,
>>+	DPLL_A_TEMP,
>>+	DPLL_A_CLOCK_ID,
>>+	DPLL_A_TYPE,
>>+	DPLL_A_PIN_IDX,
>>+	DPLL_A_PIN_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_PARENT,
>>+	DPLL_A_PIN_PARENT_IDX,
>>+	DPLL_A_PIN_RCLK_DEVICE,
>>+	DPLL_A_PIN_DPLL_CAPS,
>>+
>>+	__DPLL_A_MAX,
>>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>>+};
>>+
>>+enum {
>>+	DPLL_CMD_UNSPEC = 1,
>>+	DPLL_CMD_DEVICE_GET,
>>+	DPLL_CMD_DEVICE_SET,
>>+	DPLL_CMD_PIN_GET,
>>+	DPLL_CMD_PIN_SET,
>>+
>>+	__DPLL_CMD_MAX,
>>+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
>>+};
>>+
>>+#define DPLL_MCGRP_MONITOR	"monitor"
>>+
>>+#endif /* _UAPI_LINUX_DPLL_H */
>>--
>>2.34.1
>>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-04 21:24       ` Jakub Kicinski
  (?)
  (?)
@ 2023-05-11  7:40       ` Kubalewski, Arkadiusz
  2023-05-11  7:59         ` Jiri Pirko
  2023-05-11 15:20         ` Jakub Kicinski
  -1 siblings, 2 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11  7:40 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech, Milena, Michalik,
	Michal, linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Vadim Fedorenko

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 4, 2023 11:25 PM
>
>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>> >+definitions:
>> >+  -
>> >+    type: enum
>> >+    name: mode
>> >+    doc: |
>> >+      working-modes a dpll can support, differentiate if and how dpll
>>selects
>> >+      one of its sources to syntonize with it, valid values for
>>DPLL_A_MODE
>> >+      attribute
>> >+    entries:
>> >+      -
>> >+        name: unspec
>>
>> In general, why exactly do we need unspec values in enums and CMDs?
>> What is the usecase. If there isn't please remove.
>
>+1
>

Sure, fixed.

>> >+        doc: unspecified value
>> >+      -
>> >+        name: manual
>
>I think the documentation calls this "forced", still.
>

Yes, good catch, fixed docs.

>> >+        doc: source can be only selected by sending a request to dpll
>> >+      -
>> >+        name: automatic
>> >+        doc: highest prio, valid source, auto selected by dpll
>> >+      -
>> >+        name: holdover
>> >+        doc: dpll forced into holdover mode
>> >+      -
>> >+        name: freerun
>> >+        doc: dpll driven on system clk, no holdover available
>>
>> Remove "no holdover available". This is not a state, this is a mode
>> configuration. If holdover is or isn't available, is a runtime info.
>
>Agreed, seems a little confusing now. Should we expose the system clk
>as a pin to be able to force lock to it? Or there's some extra magic
>at play here?

In freerun you cannot lock to anything it, it just uses system clock from
one of designated chip wires (which is not a part of source pins pool) to feed
the dpll. Dpll would only stabilize that signal and pass it further.
Locking itself is some kind of magic, as it usually takes at least ~15 seconds
before it locks to a signal once it is selected.

>
>> >+      -
>> >+        name: nco
>> >+        doc: dpll driven by Numerically Controlled Oscillator
>
>Noob question, what is NCO in terms of implementation?
>We source the signal from an arbitrary pin and FW / driver does
>the control? Or we always use system refclk and then tune?
>

Documentation of chip we are using, stated NCO as similar to FREERUN, and it
runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
dividers before it reaches the output).
It doesn't count as an source pin, it uses signal form dedicated wire for
SYSTEM CLOCK.
In this case control over output frequency is done by synchronizer chip
firmware, but still it will not lock to any source pin signal.

>> >+    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: unspec
>> >+        doc: unspecified value
>> >+      -
>> >+        name: unlocked
>> >+        doc: |
>> >+          dpll was not yet locked to any valid source (or is in one of
>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>> >+      -
>> >+        name: calibrating
>> >+        doc: dpll is trying to lock to a valid signal
>> >+      -
>> >+        name: locked
>> >+        doc: dpll is locked
>> >+      -
>> >+        name: holdover
>> >+        doc: |
>> >+          dpll is in holdover state - lost a valid lock or was forced by
>> >+          selecting DPLL_MODE_HOLDOVER mode
>>
>> Is it needed to mention the holdover mode. It's slightly confusing,
>> because user might understand that the lock-status is always "holdover"
>> in case of "holdover" mode. But it could be "unlocked", can't it?
>> Perhaps I don't understand the flows there correctly :/
>
>Hm, if we want to make sure that holdover mode must result in holdover
>state then we need some extra atomicity requirements on the SET
>operation. To me it seems logical enough that after setting holdover
>mode we'll end up either in holdover or unlocked status, depending on
>lock status when request reached the HW.
>

Improved the docs:
        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 it was not, the dpll's lock-status will remain
          DPLL_LOCK_STATUS_UNLOCKED even if user requests
          DPLL_MODE_HOLDOVER)
Is that better?

What extra atomicity you have on your mind?
Do you suggest to validate and allow (in dpll_netlink.c) only for 'unlocked'
or 'holdover' states of dpll, once DPLL_MODE_HOLDOVER was successfully
requested by the user?

>> >+    render-max: true
>> >+  -
>> >+    type: const
>> >+    name: temp-divider
>> >+    value: 10
>> >+    doc: |
>> >+      temperature divider allowing userspace to calculate the
>> >+      temperature as float with single digit precision.
>> >+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>> >+      tempearture value.
>>
>> s/tempearture/temperature/
>>
>> Didn't checkpatch warn you?
>
>Also can we give it a more healthy engineering margin?
>DPLL_A_TEMP is u32, silicon melts at around 1400C,
>so we really can afford to make the divisor 1000.
>

Sure, fixed.

>> >+    name: device
>> >+    subset-of: dpll
>> >+    attributes:
>> >+      -
>> >+        name: id
>> >+        type: u32
>> >+        value: 2
>> >+      -
>> >+        name: dev-name
>> >+        type: string
>> >+      -
>> >+        name: bus-name
>> >+        type: string
>> >+      -
>> >+        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: clock-id
>> >+        type: u64
>> >+      -
>> >+        name: type
>> >+        type: u8
>> >+        enum: type
>> >+      -
>> >+        name: pin-prio
>> >+        type: u32
>> >+        value: 19
>>
>> Do you still need to pass values for a subset? That is odd. Well, I
>> think is is odd to pass anything other than names in subset definition,
>> the rest of the info is in the original attribute set definition,
>> isn't it?
>> Jakub?
>
>Probably stale code, related bug was fixed in YNL a few months back.
>Explicit value should no longer be needed.

Yes, checked it works without them, I am removing values for next version.

Thanks!
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-05 10:29         ` Jiri Pirko
  (?)
@ 2023-05-11  7:44         ` Kubalewski, Arkadiusz
  2023-05-11  8:00           ` Jiri Pirko
  -1 siblings, 1 reply; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11  7:44 UTC (permalink / raw)
  To: Jiri Pirko, Jakub Kicinski
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Vadim Fedorenko


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, May 5, 2023 12:30 PM
>
>Thu, May 04, 2023 at 11:24:51PM CEST, kuba@kernel.org wrote:
>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>
>[...]
>
>>
>>> >+    name: device
>>> >+    subset-of: dpll
>>> >+    attributes:
>>> >+      -
>>> >+        name: id
>>> >+        type: u32
>>> >+        value: 2
>>> >+      -
>>> >+        name: dev-name
>>> >+        type: string
>>> >+      -
>>> >+        name: bus-name
>>> >+        type: string
>>> >+      -
>>> >+        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: clock-id
>>> >+        type: u64
>>> >+      -
>>> >+        name: type
>>> >+        type: u8
>>> >+        enum: type
>>> >+      -
>>> >+        name: pin-prio
>>> >+        type: u32
>>> >+        value: 19
>>>
>>> Do you still need to pass values for a subset? That is odd. Well, I
>>> think is is odd to pass anything other than names in subset definition,
>>> the rest of the info is in the original attribute set definition,
>>> isn't it?
>>> Jakub?
>>
>>Probably stale code, related bug was fixed in YNL a few months back.
>>Explicit value should no longer be needed.
>
>What about the rest, like type, enum, multi-attr etc. Are they needed
>for subset? If yes, why?
>
>

It seems the name and type is needed. Without type generation scripts fails.
For now fixed with having only name/type on subset attributes.

Thanks!
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-04-28  0:20 ` Vadim Fedorenko
                   ` (10 preceding siblings ...)
  (?)
@ 2023-05-11  7:52 ` Jiri Pirko
  2023-05-25 13:01     ` Kubalewski, Arkadiusz
  -1 siblings, 1 reply; 149+ messages in thread
From: Jiri Pirko @ 2023-05-11  7:52 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>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.
>
>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

Vadim, did you try ynl monitor? I think there might be something wrong
with the yaml spec:
# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --subscribe monitor --sleep 10
Unexpected msg id done while checking for ntf nl_len = 92 (76) nl_flags = 0x0 nl_type = 19



^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:40       ` Kubalewski, Arkadiusz
@ 2023-05-11  7:59         ` Jiri Pirko
  2023-05-11 20:51           ` Kubalewski, Arkadiusz
  2023-05-11 15:20         ` Jakub Kicinski
  1 sibling, 1 reply; 149+ messages in thread
From: Jiri Pirko @ 2023-05-11  7:59 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jakub Kicinski <kuba@kernel.org>
>>Sent: Thursday, May 4, 2023 11:25 PM
>>
>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>> >+definitions:
>>> >+  -
>>> >+    type: enum
>>> >+    name: mode
>>> >+    doc: |
>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>selects
>>> >+      one of its sources to syntonize with it, valid values for
>>>DPLL_A_MODE
>>> >+      attribute
>>> >+    entries:
>>> >+      -
>>> >+        name: unspec
>>>
>>> In general, why exactly do we need unspec values in enums and CMDs?
>>> What is the usecase. If there isn't please remove.
>>
>>+1
>>
>
>Sure, fixed.
>
>>> >+        doc: unspecified value
>>> >+      -
>>> >+        name: manual
>>
>>I think the documentation calls this "forced", still.
>>
>
>Yes, good catch, fixed docs.
>
>>> >+        doc: source can be only selected by sending a request to dpll
>>> >+      -
>>> >+        name: automatic
>>> >+        doc: highest prio, valid source, auto selected by dpll
>>> >+      -
>>> >+        name: holdover
>>> >+        doc: dpll forced into holdover mode
>>> >+      -
>>> >+        name: freerun
>>> >+        doc: dpll driven on system clk, no holdover available
>>>
>>> Remove "no holdover available". This is not a state, this is a mode
>>> configuration. If holdover is or isn't available, is a runtime info.
>>
>>Agreed, seems a little confusing now. Should we expose the system clk
>>as a pin to be able to force lock to it? Or there's some extra magic
>>at play here?
>
>In freerun you cannot lock to anything it, it just uses system clock from
>one of designated chip wires (which is not a part of source pins pool) to feed
>the dpll. Dpll would only stabilize that signal and pass it further.
>Locking itself is some kind of magic, as it usually takes at least ~15 seconds
>before it locks to a signal once it is selected.
>
>>
>>> >+      -
>>> >+        name: nco
>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>
>>Noob question, what is NCO in terms of implementation?
>>We source the signal from an arbitrary pin and FW / driver does
>>the control? Or we always use system refclk and then tune?
>>
>
>Documentation of chip we are using, stated NCO as similar to FREERUN, and it

So how exactly this is different to freerun? Does user care or he would
be fine with "freerun" in this case? My point is, isn't "NCO" some
device specific thing that should be abstracted out here?


>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>dividers before it reaches the output).
>It doesn't count as an source pin, it uses signal form dedicated wire for
>SYSTEM CLOCK.
>In this case control over output frequency is done by synchronizer chip
>firmware, but still it will not lock to any source pin signal.
>
>>> >+    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: unspec
>>> >+        doc: unspecified value
>>> >+      -
>>> >+        name: unlocked
>>> >+        doc: |
>>> >+          dpll was not yet locked to any valid source (or is in one of
>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>> >+      -
>>> >+        name: calibrating
>>> >+        doc: dpll is trying to lock to a valid signal
>>> >+      -
>>> >+        name: locked
>>> >+        doc: dpll is locked
>>> >+      -
>>> >+        name: holdover
>>> >+        doc: |
>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>
>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>> because user might understand that the lock-status is always "holdover"
>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>> Perhaps I don't understand the flows there correctly :/
>>
>>Hm, if we want to make sure that holdover mode must result in holdover
>>state then we need some extra atomicity requirements on the SET
>>operation. To me it seems logical enough that after setting holdover
>>mode we'll end up either in holdover or unlocked status, depending on
>>lock status when request reached the HW.
>>
>
>Improved the docs:
>        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 it was not, the dpll's lock-status will remain

"if it was not" does not really cope with the sentence above that. Could
you iron-out the phrasing a bit please?


>          DPLL_LOCK_STATUS_UNLOCKED even if user requests
>          DPLL_MODE_HOLDOVER)
>Is that better?
>
>What extra atomicity you have on your mind?
>Do you suggest to validate and allow (in dpll_netlink.c) only for 'unlocked'
>or 'holdover' states of dpll, once DPLL_MODE_HOLDOVER was successfully
>requested by the user?
>
>>> >+    render-max: true
>>> >+  -
>>> >+    type: const
>>> >+    name: temp-divider
>>> >+    value: 10
>>> >+    doc: |
>>> >+      temperature divider allowing userspace to calculate the
>>> >+      temperature as float with single digit precision.
>>> >+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>>> >+      tempearture value.
>>>
>>> s/tempearture/temperature/
>>>
>>> Didn't checkpatch warn you?
>>
>>Also can we give it a more healthy engineering margin?
>>DPLL_A_TEMP is u32, silicon melts at around 1400C,
>>so we really can afford to make the divisor 1000.
>>
>
>Sure, fixed.
>
>>> >+    name: device
>>> >+    subset-of: dpll
>>> >+    attributes:
>>> >+      -
>>> >+        name: id
>>> >+        type: u32
>>> >+        value: 2
>>> >+      -
>>> >+        name: dev-name
>>> >+        type: string
>>> >+      -
>>> >+        name: bus-name
>>> >+        type: string
>>> >+      -
>>> >+        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: clock-id
>>> >+        type: u64
>>> >+      -
>>> >+        name: type
>>> >+        type: u8
>>> >+        enum: type
>>> >+      -
>>> >+        name: pin-prio
>>> >+        type: u32
>>> >+        value: 19
>>>
>>> Do you still need to pass values for a subset? That is odd. Well, I
>>> think is is odd to pass anything other than names in subset definition,
>>> the rest of the info is in the original attribute set definition,
>>> isn't it?
>>> Jakub?
>>
>>Probably stale code, related bug was fixed in YNL a few months back.
>>Explicit value should no longer be needed.
>
>Yes, checked it works without them, I am removing values for next version.
>
>Thanks!
>Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:44         ` Kubalewski, Arkadiusz
@ 2023-05-11  8:00           ` Jiri Pirko
  2023-05-11 14:55             ` Jakub Kicinski
  0 siblings, 1 reply; 149+ messages in thread
From: Jiri Pirko @ 2023-05-11  8:00 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 11, 2023 at 09:44:30AM CEST, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, May 5, 2023 12:30 PM
>>
>>Thu, May 04, 2023 at 11:24:51PM CEST, kuba@kernel.org wrote:
>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>
>>[...]
>>
>>>
>>>> >+    name: device
>>>> >+    subset-of: dpll
>>>> >+    attributes:
>>>> >+      -
>>>> >+        name: id
>>>> >+        type: u32
>>>> >+        value: 2
>>>> >+      -
>>>> >+        name: dev-name
>>>> >+        type: string
>>>> >+      -
>>>> >+        name: bus-name
>>>> >+        type: string
>>>> >+      -
>>>> >+        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: clock-id
>>>> >+        type: u64
>>>> >+      -
>>>> >+        name: type
>>>> >+        type: u8
>>>> >+        enum: type
>>>> >+      -
>>>> >+        name: pin-prio
>>>> >+        type: u32
>>>> >+        value: 19
>>>>
>>>> Do you still need to pass values for a subset? That is odd. Well, I
>>>> think is is odd to pass anything other than names in subset definition,
>>>> the rest of the info is in the original attribute set definition,
>>>> isn't it?
>>>> Jakub?
>>>
>>>Probably stale code, related bug was fixed in YNL a few months back.
>>>Explicit value should no longer be needed.
>>
>>What about the rest, like type, enum, multi-attr etc. Are they needed
>>for subset? If yes, why?
>>
>>
>
>It seems the name and type is needed. Without type generation scripts fails.
>For now fixed with having only name/type on subset attributes.

Okay. Jakub, any idea why "type" is needed here?


>
>Thanks!
>Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:38     ` Kubalewski, Arkadiusz
@ 2023-05-11  8:14       ` Jiri Pirko
  2023-05-11 20:53         ` Kubalewski, Arkadiusz
  2023-05-11 15:26       ` Jakub Kicinski
  1 sibling, 1 reply; 149+ messages in thread
From: Jiri Pirko @ 2023-05-11  8:14 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 11, 2023 at 09:38:04AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, May 4, 2023 2:03 PM
>>
>>Fri, Apr 28, 2023 at 02:20:02AM CEST, vadfed@meta.com wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>>Add a protocol spec for DPLL.
>>>Add code generated from the spec.
>>>
>>>Signed-off-by: Jakub Kicinski <kuba@kernel.org>
>>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>>---
>>> Documentation/netlink/specs/dpll.yaml | 472 ++++++++++++++++++++++++++
>>> drivers/dpll/dpll_nl.c                | 126 +++++++
>>> drivers/dpll/dpll_nl.h                |  42 +++
>>> include/uapi/linux/dpll.h             | 202 +++++++++++
>>> 4 files changed, 842 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..67ca0f6cf2d5
>>>--- /dev/null
>>>+++ b/Documentation/netlink/specs/dpll.yaml
>>>@@ -0,0 +1,472 @@
>>>+# 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 sources to syntonize with it, valid values for DPLL_A_MODE
>>>+      attribute
>>>+    entries:
>>>+      -
>>>+        name: unspec
>>
>>In general, why exactly do we need unspec values in enums and CMDs?
>>What is the usecase. If there isn't please remove.
>>
>
>Sure, fixed.
>
>>
>>>+        doc: unspecified value
>>>+      -
>>>+        name: manual
>>>+        doc: source can be only selected by sending a request to dpll
>>>+      -
>>>+        name: automatic
>>>+        doc: highest prio, valid source, auto selected by dpll
>>>+      -
>>>+        name: holdover
>>>+        doc: dpll forced into holdover mode
>>>+      -
>>>+        name: freerun
>>>+        doc: dpll driven on system clk, no holdover available
>>
>>Remove "no holdover available". This is not a state, this is a mode
>>configuration. If holdover is or isn't available, is a runtime info.
>>
>
>Fiexd.
>
>>
>>>+      -
>>>+        name: nco
>>>+        doc: dpll driven by Numerically Controlled Oscillator
>>>+    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: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: unlocked
>>>+        doc: |
>>>+          dpll was not yet locked to any valid source (or is in one of
>>>+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>+      -
>>>+        name: calibrating
>>>+        doc: dpll is trying to lock to a valid signal
>>>+      -
>>>+        name: locked
>>>+        doc: dpll is locked
>>>+      -
>>>+        name: holdover
>>>+        doc: |
>>>+          dpll is in holdover state - lost a valid lock or was forced by
>>>+          selecting DPLL_MODE_HOLDOVER mode
>>
>>Is it needed to mention the holdover mode. It's slightly confusing,
>>because user might understand that the lock-status is always "holdover"
>>in case of "holdover" mode. But it could be "unlocked", can't it?
>>Perhaps I don't understand the flows there correctly :/
>>
>
>Yes, it could be unlocked even when user requests the 'holdover' mode, i.e.
>when the dpll was not locked to a valid source before requesting the mode.
>Improved the docs:
>        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 it was not, the dpll's lock-status will remain
>          DPLL_LOCK_STATUS_UNLOCKED even if user requests
>          DPLL_MODE_HOLDOVER)
>Is that better?

See my comment to this in the other branch of this thread.


>
>>
>>>+    render-max: true
>>>+  -
>>>+    type: const
>>>+    name: temp-divider
>>>+    value: 10
>>>+    doc: |
>>>+      temperature divider allowing userspace to calculate the
>>>+      temperature as float with single digit precision.
>>>+      Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>>>+      tempearture value.
>>
>>s/tempearture/temperature/
>>
>>Didn't checkpatch warn you?
>>
>
>Fixed, thanks!
>No, I don't think it did.
>
>>
>>>+      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: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: pps
>>>+        doc: dpll produces Pulse-Per-Second signal
>>>+      -
>>>+        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: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: mux
>>>+        doc: aggregates another layer of selectable pins
>>>+      -
>>>+        name: ext
>>>+        doc: external source
>>>+      -
>>>+        name: synce-eth-port
>>>+        doc: ethernet port PHY's recovered clock
>>>+      -
>>>+        name: int-oscillator
>>>+        doc: device internal oscillator
>>
>>Is this somehow related to the mode "nco" (Numerically Controlled
>>Oscillator)?
>>
>
>Yes.

How? Why do we need to expose it as a pin then?


>
>>
>>
>>>+      -
>>>+        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: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: source
>>>+        doc: pin used as a source of a signal
>>>+      -
>>>+        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-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: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: connected
>>>+        doc: pin connected, active source of phase locked loop
>>>+      -
>>>+        name: disconnected
>>>+        doc: pin disconnected, not considered as a valid source
>>>+      -
>>>+        name: selectable
>>>+        doc: pin enabled for automatic source 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
>>>+  -
>>>+    type: enum
>>>+    name: event
>>>+    doc: events of dpll generic netlink family
>>>+    entries:
>>>+      -
>>>+        name: unspec
>>>+        doc: invalid event type
>>>+      -
>>>+        name: device-create
>>>+        doc: dpll device created
>>>+      -
>>>+        name: device-delete
>>>+        doc: dpll device deleted
>>>+      -
>>>+        name: device-change
>>
>>Please have a separate create/delete/change values for pins.
>>
>
>Makes sense, but details, pin creation doesn't occur from uAPI perspective,
>as the pins itself are not visible to the user. They are visible after they
>are registered with a device, thus we would have to do something like:
>- pin-register
>- pin-unregister
>- pin-change
>
>Does it make sense?

From perspective of user, it is "creation/new" or "deletion/del".
Object appears of dissapears in UAPI, no matter how this is implemented
in kernel. If you call it register/unregister, that exposes unnecessary
internal kernel notation.

No strong feeling though if you insist on register/unregister, it just
sounds odd and funny.

Anyway, one way or another, be in-sync naming wise with device events.



>
>>
>>>+        doc: |
>>>+          attribute of dpll device or pin changed, reason is to be found
>>>with
>>>+          an attribute type (DPLL_A_*) received with the event
>>>+
>>>+
>>>+attribute-sets:
>>>+  -
>>>+    name: dpll
>>>+    enum-name: dplla
>>>+    attributes:
>>>+      -
>>>+        name: device
>>>+        type: nest
>>>+        value: 1
>>
>>Why not 0?
>>
>
>Sorry I don't recall what exact technical reasons are behind it, but all
>netlink attributes I have found have 0 value attribute unused/unspec.

I don't see why that is needed, I may be missing something though.
Up to you.


>
>>Also, Plese don't have this attr as a first one. It is related to
>>PIN_GET/SET cmd, it should be somewhere among related attributes.
>>
>>Definitelly, the handle ATTR/ATTTs should be the first one/ones.
>>
>
>Sure, fixed.
>
>>
>>
>>>+        multi-attr: true
>>>+        nested-attributes: device
>>>+      -
>>>+        name: id
>>>+        type: u32
>>>+      -
>>>+        name: dev-name
>>>+        type: string
>>>+      -
>>>+        name: bus-name
>>>+        type: string
>>>+      -
>>>+        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: clock-id
>>>+        type: u64
>>>+      -
>>>+        name: type
>>>+        type: u8
>>>+        enum: type
>>>+      -
>>>+        name: pin-idx
>>>+        type: u32
>>>+      -
>>>+        name: pin-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-parent
>>>+        type: nest
>>>+        multi-attr: true
>>>+        nested-attributes: pin-parent
>>>+      -
>>>+        name: pin-parent-idx
>>>+        type: u32
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>>+      -
>>>+        name: pin-dpll-caps
>>>+        type: u32
>>>+  -
>>>+    name: device
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: id
>>>+        type: u32
>>>+        value: 2
>>>+      -
>>>+        name: dev-name
>>>+        type: string
>>>+      -
>>>+        name: bus-name
>>>+        type: string
>>>+      -
>>>+        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: clock-id
>>>+        type: u64
>>>+      -
>>>+        name: type
>>>+        type: u8
>>>+        enum: type
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+        value: 19
>>
>>Do you still need to pass values for a subset? That is odd. Well, I
>>think is is odd to pass anything other than names in subset definition,
>>the rest of the info is in the original attribute set definition,
>>isn't it?
>>Jakub?
>>
>
>Yes it is fixed, I will remove those.
>
>>
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+  -
>>>+    name: pin-parent
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        value: 20
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent-idx
>>>+        type: u32
>>>+        value: 22
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>>+  -
>>>+    name: pin-frequency-range
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: pin-frequency-min
>>>+        type: u64
>>>+        value: 17
>>>+      -
>>>+        name: pin-frequency-max
>>>+        type: u64
>>>+
>>>+operations:
>>>+  list:
>>>+    -
>>>+      name: unspec
>>>+      doc: unused
>>>+
>>>+    -
>>>+      name: device-get
>>>+      doc: |
>>>+        Get list of DPLL devices (dump) or attributes of a single dpll
>>>device
>>>+      attribute-set: dpll
>>>+      flags: [ admin-perm ]
>>
>>I may be missing something, but why do you enforce adming perm for
>>get/dump cmds?
>>
>
>Yes, security reasons, we don't want regular users to spam-query the driver
>ops. Also explained in docs:
>All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
>any spamming/D.o.S. from unauthorized userspace applications.

Hmm, I wonder why other read cmds usually don't need this. In fact,
is there some read netlink cmd in kernel now which needs it?


>
>>
>>>+
>>>+      do:
>>>+        pre: dpll-pre-doit
>>>+        post: dpll-post-doit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+        reply:
>>>+          attributes:
>>>+            - device
>>>+
>>>+      dump:
>>>+        pre: dpll-pre-dumpit
>>>+        post: dpll-post-dumpit
>>>+        reply:
>>>+          attributes:
>>>+            - device
>>
>>I might be missing something, but this means "device" netdev attribute
>>DPLL_A_DEVICE, right? If yes, that is incorrect and you should list all
>>the device attrs.
>>
>
>Actually this means that attributes expected in response to this command are
>from `device` subset.
>But I see your point, will make `device` subset only for pin's nested
>attributes, and here will list device attributes.

Yes, that is my point. The fix you describes sounds fine.


>
>>
>>>+
>>>+    -
>>>+      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
>>>+            - bus-name
>>>+            - dev-name
>>>+            - mode
>>>+
>>>+    -
>>>+      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
>>>+            - bus-name
>>>+            - dev-name
>>>+            - pin-idx
>>>+        reply: &pin-attrs
>>>+          attributes:
>>>+            - pin-idx
>>>+            - pin-label
>>>+            - pin-type
>>>+            - pin-direction
>>>+            - pin-frequency
>>>+            - pin-frequency-supported
>>>+            - pin-parent
>>>+            - pin-rclk-device
>>>+            - pin-dpll-caps
>>>+            - device
>>>+
>>>+      dump:
>>>+        pre: dpll-pin-pre-dumpit
>>>+        post: dpll-pin-post-dumpit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+        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
>>>+            - bus-name
>>>+            - dev-name
>>>+            - pin-idx
>>>+            - pin-frequency
>>>+            - pin-direction
>>>+            - pin-prio
>>>+            - pin-state
>>>+            - pin-parent-idx
>>>+
>>>+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..2f8643f401b0
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.c
>>>@@ -0,0 +1,126 @@
>>>+// 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 <linux/dpll.h>
>>>+
>>>+/* DPLL_CMD_DEVICE_GET - do */
>>>+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_BUS_NAME
>>>+ 1] = {
>>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_DEV_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_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_MODE] = NLA_POLICY_MAX(NLA_U8, 5),
>>
>>I know it is a matter of the generator script, still have to note it
>>hurts my eyes to see "5" here :)
>>
>
>Yes, that's true.
>
>Thanks!
>Arkadiusz
>
>>
>>>+};
>>>+
>>>+/* DPLL_CMD_PIN_GET - do */
>>>+static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_IDX +
>>>1] = {
>>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_PIN_IDX] = { .type = NLA_U32, },
>>>+};
>>>+
>>>+/* DPLL_CMD_PIN_GET - dump */
>>>+static const struct nla_policy
>>>dpll_pin_get_dump_nl_policy[DPLL_A_BUS_NAME + 1] = {
>>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>>+};
>>>+
>>>+/* DPLL_CMD_PIN_SET - do */
>>>+static const struct nla_policy
>>>dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_IDX + 1] = {
>>>+	[DPLL_A_ID] = { .type = NLA_U32, },
>>>+	[DPLL_A_BUS_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_DEV_NAME] = { .type = NLA_NUL_STRING, },
>>>+	[DPLL_A_PIN_IDX] = { .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_IDX] = { .type = NLA_U32, },
>>>+};
>>>+
>>>+/* Ops table for dpll */
>>>+static const struct genl_split_ops dpll_nl_ops[] = {
>>>+	{
>>>+		.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_BUS_NAME,
>>>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>>>+	},
>>>+	{
>>>+		.cmd	= DPLL_CMD_DEVICE_GET,
>>>+		.start	= dpll_pre_dumpit,
>>>+		.dumpit	= dpll_nl_device_get_dumpit,
>>>+		.done	= dpll_post_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_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_IDX,
>>>+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
>>>+	},
>>>+	{
>>>+		.cmd		= DPLL_CMD_PIN_GET,
>>>+		.start		= dpll_pin_pre_dumpit,
>>>+		.dumpit		= dpll_nl_pin_get_dumpit,
>>>+		.done		= dpll_pin_post_dumpit,
>>>+		.policy		= dpll_pin_get_dump_nl_policy,
>>>+		.maxattr	= DPLL_A_BUS_NAME,
>>>+		.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_IDX,
>>>+		.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..57ab2da562ba
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.h
>>>@@ -0,0 +1,42 @@
>>>+/* 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 <linux/dpll.h>
>>>+
>>>+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_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_pre_dumpit(struct netlink_callback *cb);
>>>+int dpll_pin_pre_dumpit(struct netlink_callback *cb);
>>>+int dpll_post_dumpit(struct netlink_callback *cb);
>>>+int dpll_pin_post_dumpit(struct netlink_callback *cb);
>>>+
>>>+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_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..e188bc189754
>>>--- /dev/null
>>>+++ b/include/uapi/linux/dpll.h
>>>@@ -0,0 +1,202 @@
>>>+/* 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 sources to syntonize with it, valid values for
>>>+ *   DPLL_A_MODE attribute
>>>+ * @DPLL_MODE_UNSPEC: unspecified value
>>>+ * @DPLL_MODE_MANUAL: source can be only selected by sending a request to
>>>dpll
>>>+ * @DPLL_MODE_AUTOMATIC: highest prio, valid source, auto selected by dpll
>>>+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
>>>+ * @DPLL_MODE_FREERUN: dpll driven on system clk, no holdover available
>>>+ * @DPLL_MODE_NCO: dpll driven by Numerically Controlled Oscillator
>>>+ */
>>>+enum dpll_mode {
>>>+	DPLL_MODE_UNSPEC,
>>>+	DPLL_MODE_MANUAL,
>>>+	DPLL_MODE_AUTOMATIC,
>>>+	DPLL_MODE_HOLDOVER,
>>>+	DPLL_MODE_FREERUN,
>>>+	DPLL_MODE_NCO,
>>>+
>>>+	__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_UNSPEC: unspecified value
>>>+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid
>>>source (or
>>>+ *   is in one of modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>+ * @DPLL_LOCK_STATUS_CALIBRATING: dpll is trying to lock to a valid
>>>signal
>>>+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked
>>>+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid
>>>lock or
>>>+ *   was forced by selecting DPLL_MODE_HOLDOVER mode
>>>+ */
>>>+enum dpll_lock_status {
>>>+	DPLL_LOCK_STATUS_UNSPEC,
>>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>>+	DPLL_LOCK_STATUS_LOCKED,
>>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>>+
>>>+	__DPLL_LOCK_STATUS_MAX,
>>>+	DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
>>>+};
>>>+
>>>+#define DPLL_TEMP_DIVIDER	10
>>>+
>>>+/**
>>>+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
>>>+ * @DPLL_TYPE_UNSPEC: unspecified value
>>>+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
>>>+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
>>>+ */
>>>+enum dpll_type {
>>>+	DPLL_TYPE_UNSPEC,
>>>+	DPLL_TYPE_PPS,
>>>+	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_UNSPEC: unspecified value
>>>+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
>>>+ * @DPLL_PIN_TYPE_EXT: external source
>>>+ * @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_UNSPEC,
>>>+	DPLL_PIN_TYPE_MUX,
>>>+	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_UNSPEC: unspecified value
>>>+ * @DPLL_PIN_DIRECTION_SOURCE: pin used as a source of a signal
>>>+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
>>>+ */
>>>+enum dpll_pin_direction {
>>>+	DPLL_PIN_DIRECTION_UNSPEC,
>>>+	DPLL_PIN_DIRECTION_SOURCE,
>>>+	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_MHZ	10000000
>>>+
>>>+/**
>>>+ * enum dpll_pin_state - defines possible states of a pin, valid values for
>>>+ *   DPLL_A_PIN_STATE attribute
>>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active source of phase
>>locked loop
>>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
>>>+ *   source
>>>+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic source selection
>>>+ */
>>>+enum dpll_pin_state {
>>>+	DPLL_PIN_STATE_UNSPEC,
>>>+	DPLL_PIN_STATE_CONNECTED,
>>>+	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_event - events of dpll generic netlink family
>>>+ * @DPLL_EVENT_UNSPEC: invalid event type
>>>+ * @DPLL_EVENT_DEVICE_CREATE: dpll device created
>>>+ * @DPLL_EVENT_DEVICE_DELETE: dpll device deleted
>>>+ * @DPLL_EVENT_DEVICE_CHANGE: attribute of dpll device or pin changed,
>>>reason
>>>+ *   is to be found with an attribute type (DPLL_A_*) received with the
>>>event
>>>+ */
>>>+enum dpll_event {
>>>+	DPLL_EVENT_UNSPEC,
>>>+	DPLL_EVENT_DEVICE_CREATE,
>>>+	DPLL_EVENT_DEVICE_DELETE,
>>>+	DPLL_EVENT_DEVICE_CHANGE,
>>>+};
>>>+
>>>+enum dplla {
>>>+	DPLL_A_DEVICE = 1,
>>>+	DPLL_A_ID,
>>>+	DPLL_A_DEV_NAME,
>>>+	DPLL_A_BUS_NAME,
>>>+	DPLL_A_MODE,
>>>+	DPLL_A_MODE_SUPPORTED,
>>>+	DPLL_A_LOCK_STATUS,
>>>+	DPLL_A_TEMP,
>>>+	DPLL_A_CLOCK_ID,
>>>+	DPLL_A_TYPE,
>>>+	DPLL_A_PIN_IDX,
>>>+	DPLL_A_PIN_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_PARENT,
>>>+	DPLL_A_PIN_PARENT_IDX,
>>>+	DPLL_A_PIN_RCLK_DEVICE,
>>>+	DPLL_A_PIN_DPLL_CAPS,
>>>+
>>>+	__DPLL_A_MAX,
>>>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>>>+};
>>>+
>>>+enum {
>>>+	DPLL_CMD_UNSPEC = 1,
>>>+	DPLL_CMD_DEVICE_GET,
>>>+	DPLL_CMD_DEVICE_SET,
>>>+	DPLL_CMD_PIN_GET,
>>>+	DPLL_CMD_PIN_SET,
>>>+
>>>+	__DPLL_CMD_MAX,
>>>+	DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
>>>+};
>>>+
>>>+#define DPLL_MCGRP_MONITOR	"monitor"
>>>+
>>>+#endif /* _UAPI_LINUX_DPLL_H */
>>>--
>>>2.34.1
>>>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface
  2023-05-05 15:29         ` Jakub Kicinski
  (?)
@ 2023-05-11 10:23         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11 10:23 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Friday, May 5, 2023 5:30 PM
>
>On Fri, 5 May 2023 14:16:53 +0100 Vadim Fedorenko wrote:
>> >> +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. ``PIN_FREQUENCY``, ``PIN_DIRECTION``),
>> >
>> > Why is direction set on a pin? We can't chain DPLLs?
>>
>> We can chain DPLLs using pins only. We don't have any interface to
>> configure 2 pins to connect 2 different DPLLs to each other at the same
>> time. The configuration should take care of one pin being input and
>> other one being output. That's why we have direction property attached
>> to the pin, not the DPLL itself.
>
>Makes sense.

Hmmm, actually we already can register the same pin with 2 dplls, and it could
identify itself as an input for one dpll and as an output for the other. It
makes sense as change on one pin would again inform both dplls.

The dpll_pin_direction_get/set callbacks are already called with dpll, so
we are good, only one change would be needed is to move PIN_DIRECTION to the
pin-dpll configuration tuples on `pin-get`.

Thus, actually it looks like a bug now:
- pin-dpll tuple may report different direction for each dpll pin was
registered with
- userspace see this as pin specific property, but it would return different
values depending on which dpll was given as arg for pin-get command.

I will fix it, as it relates to the modifying spec in first place.

>
>> >> +Device driver implementation
>> >> +============================
>> >> +
>> >> +Device is allocated by ``dpll_device_get`` call. Second call with the
>> >> +same arguments doesn't create new object but provides pointer to
>> >> +previously created device for given arguments, it also increase 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.
>> >
>> > You can add () after the function name and render the kdoc at the end
>> > of this doc. The `` marking will then be unnecessary.
>> >
>> Mmm... any examples of such a way of creating documentation? I was
>> following tls*.rst style, but without copying code-blocks.
>
>net_dim.rst, maybe?  driver.rst ? Feel free to ping me 1:1 if you're
>struggling, it should be fairly straightforward.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  8:00           ` Jiri Pirko
@ 2023-05-11 14:55             ` Jakub Kicinski
  0 siblings, 0 replies; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-11 14:55 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Vadim Fedorenko

On Thu, 11 May 2023 10:00:35 +0200 Jiri Pirko wrote:
>> It seems the name and type is needed. Without type generation scripts fails.
>> For now fixed with having only name/type on subset attributes.  
> 
> Okay. Jakub, any idea why "type" is needed here?

I don't remember.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:40       ` Kubalewski, Arkadiusz
  2023-05-11  7:59         ` Jiri Pirko
@ 2023-05-11 15:20         ` Jakub Kicinski
  2023-05-11 20:53           ` Kubalewski, Arkadiusz
  1 sibling, 1 reply; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-11 15:20 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

On Thu, 11 May 2023 07:40:26 +0000 Kubalewski, Arkadiusz wrote:
> >> Remove "no holdover available". This is not a state, this is a mode
> >> configuration. If holdover is or isn't available, is a runtime info.  
> >
> >Agreed, seems a little confusing now. Should we expose the system clk
> >as a pin to be able to force lock to it? Or there's some extra magic
> >at play here?  
> 
> In freerun you cannot lock to anything it, it just uses system clock from
> one of designated chip wires (which is not a part of source pins pool) to feed
> the dpll. Dpll would only stabilize that signal and pass it further.
> Locking itself is some kind of magic, as it usually takes at least ~15 seconds
> before it locks to a signal once it is selected.

Okay, I guess that makes sense.

I was wondering if there may be a DPLLs which allow other input clocks
to bypass the PLL logic, and output purely a stabilized signal. In
which case we should model this as a generic PLL bypass, FREERUN being
just one special case where we're bypassing with the system clock.

But that may well be a case of "software guy thinking", so if nobody
thinks this can happen in practice we can keep FREERUN.

> >Noob question, what is NCO in terms of implementation?
> >We source the signal from an arbitrary pin and FW / driver does
> >the control? Or we always use system refclk and then tune?
> >  
> 
> Documentation of chip we are using, stated NCO as similar to FREERUN, and it
> runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
> dividers before it reaches the output).
> It doesn't count as an source pin, it uses signal form dedicated wire for
> SYSTEM CLOCK.
> In this case control over output frequency is done by synchronizer chip
> firmware, but still it will not lock to any source pin signal.

Reading wikipedia it sounds like NCO is just a way of generating 
a waveform from synchronous logic.

Does the DPLL not allow changing clock frequency when locked?
I.e. feeding it one frequency and outputting another?
Because I think that'd be done by an NCO, no?

> >> Is it needed to mention the holdover mode. It's slightly confusing,
> >> because user might understand that the lock-status is always "holdover"
> >> in case of "holdover" mode. But it could be "unlocked", can't it?
> >> Perhaps I don't understand the flows there correctly :/  
> >
> >Hm, if we want to make sure that holdover mode must result in holdover
> >state then we need some extra atomicity requirements on the SET
> >operation. To me it seems logical enough that after setting holdover
> >mode we'll end up either in holdover or unlocked status, depending on
> >lock status when request reached the HW.
> >  
> 
> Improved the docs:
>         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 it was not, the dpll's lock-status will remain
>           DPLL_LOCK_STATUS_UNLOCKED even if user requests
>           DPLL_MODE_HOLDOVER)
> Is that better?

Yes, modulo breaking it up into sentences, as Jiri says.

> What extra atomicity you have on your mind?
> Do you suggest to validate and allow (in dpll_netlink.c) only for 'unlocked'
> or 'holdover' states of dpll, once DPLL_MODE_HOLDOVER was successfully
> requested by the user?

No, I was saying that making sure that we end up in holdover (rather
than unlocked) when user requested holdover is hard, and we shouldn't 
even try to implement that.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:38     ` Kubalewski, Arkadiusz
  2023-05-11  8:14       ` Jiri Pirko
@ 2023-05-11 15:26       ` Jakub Kicinski
  2023-05-11 20:54         ` Kubalewski, Arkadiusz
  1 sibling, 1 reply; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-11 15:26 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jiri Pirko, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

On Thu, 11 May 2023 07:38:04 +0000 Kubalewski, Arkadiusz wrote:
> >>+  -
> >>+    type: enum
> >>+    name: event
> >>+    doc: events of dpll generic netlink family
> >>+    entries:
> >>+      -
> >>+        name: unspec
> >>+        doc: invalid event type
> >>+      -
> >>+        name: device-create
> >>+        doc: dpll device created
> >>+      -
> >>+        name: device-delete
> >>+        doc: dpll device deleted
> >>+      -
> >>+        name: device-change  
> >
> >Please have a separate create/delete/change values for pins.
> >  
> 
> Makes sense, but details, pin creation doesn't occur from uAPI perspective,
> as the pins itself are not visible to the user. They are visible after they
> are registered with a device, thus we would have to do something like:
> - pin-register
> - pin-unregister
> - pin-change
> 
> Does it make sense?

I missed this, notifications should be declared under operations.

Please look at netdev.yaml for an example.

I thought about implementing this model where events are separate
explicitly but I think it's an unnecessary complication.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  7:59         ` Jiri Pirko
@ 2023-05-11 20:51           ` Kubalewski, Arkadiusz
  2023-05-15  9:30               ` Jiri Pirko
  0 siblings, 1 reply; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11 20:51 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, May 11, 2023 10:00 AM
>
>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jakub Kicinski <kuba@kernel.org>
>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>
>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>> >+definitions:
>>>> >+  -
>>>> >+    type: enum
>>>> >+    name: mode
>>>> >+    doc: |
>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>selects
>>>> >+      one of its sources to syntonize with it, valid values for
>>>>DPLL_A_MODE
>>>> >+      attribute
>>>> >+    entries:
>>>> >+      -
>>>> >+        name: unspec
>>>>
>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>> What is the usecase. If there isn't please remove.
>>>
>>>+1
>>>
>>
>>Sure, fixed.
>>
>>>> >+        doc: unspecified value
>>>> >+      -
>>>> >+        name: manual
>>>
>>>I think the documentation calls this "forced", still.
>>>
>>
>>Yes, good catch, fixed docs.
>>
>>>> >+        doc: source can be only selected by sending a request to dpll
>>>> >+      -
>>>> >+        name: automatic
>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>> >+      -
>>>> >+        name: holdover
>>>> >+        doc: dpll forced into holdover mode
>>>> >+      -
>>>> >+        name: freerun
>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>
>>>> Remove "no holdover available". This is not a state, this is a mode
>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>
>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>at play here?
>>
>>In freerun you cannot lock to anything it, it just uses system clock from
>>one of designated chip wires (which is not a part of source pins pool) to
>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>Locking itself is some kind of magic, as it usually takes at least ~15
>>seconds before it locks to a signal once it is selected.
>>
>>>
>>>> >+      -
>>>> >+        name: nco
>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>
>>>Noob question, what is NCO in terms of implementation?
>>>We source the signal from an arbitrary pin and FW / driver does
>>>the control? Or we always use system refclk and then tune?
>>>
>>
>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>it
>
>So how exactly this is different to freerun? Does user care or he would
>be fine with "freerun" in this case? My point is, isn't "NCO" some
>device specific thing that should be abstracted out here?
>

Sure, it is device specific, some synchronizing circuits would have this
capability, while others would not.
Should be abstracted out? It is a good question.. shall user know that he is in
freerun with possibility to control the frequency or not?
Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
supporting multiple output frequencies.
How the one would know if those frequencies are supported only in
MANUAL/AUTOMATIC modes or also in the FREERUN mode?
In other words: As the user can I change a frequency of a dpll if active
mode is FREERUN?

I would say it is better to have such mode, we could argue on naming though.

>
>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>dividers before it reaches the output).
>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>SYSTEM CLOCK.
>>In this case control over output frequency is done by synchronizer chip
>>firmware, but still it will not lock to any source pin signal.
>>
>>>> >+    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: unspec
>>>> >+        doc: unspecified value
>>>> >+      -
>>>> >+        name: unlocked
>>>> >+        doc: |
>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>> >+      -
>>>> >+        name: calibrating
>>>> >+        doc: dpll is trying to lock to a valid signal
>>>> >+      -
>>>> >+        name: locked
>>>> >+        doc: dpll is locked
>>>> >+      -
>>>> >+        name: holdover
>>>> >+        doc: |
>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>
>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>> because user might understand that the lock-status is always "holdover"
>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>> Perhaps I don't understand the flows there correctly :/
>>>
>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>state then we need some extra atomicity requirements on the SET
>>>operation. To me it seems logical enough that after setting holdover
>>>mode we'll end up either in holdover or unlocked status, depending on
>>>lock status when request reached the HW.
>>>
>>
>>Improved the docs:
>>        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 it was not, the dpll's lock-status will remain
>
>"if it was not" does not really cope with the sentence above that. Could
>you iron-out the phrasing a bit please?


Hmmm,
        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)

Hope this is better?


Thank you!
Arkadiusz

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11  8:14       ` Jiri Pirko
@ 2023-05-11 20:53         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11 20:53 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, May 11, 2023 10:14 AM
>
>Thu, May 11, 2023 at 09:38:04AM CEST, arkadiusz.kubalewski@intel.com wrote:

[...]

>>>>+      -
>>>>+        name: holdover
>>>>+        doc: |
>>>>+          dpll is in holdover state - lost a valid lock or was forced by
>>>>+          selecting DPLL_MODE_HOLDOVER mode
>>>
>>>Is it needed to mention the holdover mode. It's slightly confusing,
>>>because user might understand that the lock-status is always "holdover"
>>>in case of "holdover" mode. But it could be "unlocked", can't it?
>>>Perhaps I don't understand the flows there correctly :/
>>>
>>
>>Yes, it could be unlocked even when user requests the 'holdover' mode,
>i.e.
>>when the dpll was not locked to a valid source before requesting the mode.
>>Improved the docs:
>>        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 it was not, the dpll's lock-status will remain
>>          DPLL_LOCK_STATUS_UNLOCKED even if user requests
>>          DPLL_MODE_HOLDOVER)
>>Is that better?
>
>See my comment to this in the other branch of this thread.
>

Sure, gonna reply there.

[...]

>>>>+      -
>>>>+        name: int-oscillator
>>>>+        doc: device internal oscillator
>>>
>>>Is this somehow related to the mode "nco" (Numerically Controlled
>>>Oscillator)?
>>>
>>
>>Yes.
>
>How? Why do we need to expose it as a pin then?
>

Sorry, I messed up something with that answer..
It is not related to NCO (as NCO uses SYSTEM CLOCK to produce frequency).

It is a type of a pin which source or output is somehow internal. I.e.
our dpll's can syntonize to the 1 PPS clock signal produced by network chip.
As for other use-cases it could serve as way of having one or more oscillators
on board connected to input pins.

[...]

>>>>+    type: enum
>>>>+    name: event
>>>>+    doc: events of dpll generic netlink family
>>>>+    entries:
>>>>+      -
>>>>+        name: unspec
>>>>+        doc: invalid event type
>>>>+      -
>>>>+        name: device-create
>>>>+        doc: dpll device created
>>>>+      -
>>>>+        name: device-delete
>>>>+        doc: dpll device deleted
>>>>+      -
>>>>+        name: device-change
>>>
>>>Please have a separate create/delete/change values for pins.
>>>
>>
>>Makes sense, but details, pin creation doesn't occur from uAPI perspective,
>>as the pins itself are not visible to the user. They are visible after they
>>are registered with a device, thus we would have to do something like:
>>- pin-register
>>- pin-unregister
>>- pin-change
>>
>>Does it make sense?
>
>From perspective of user, it is "creation/new" or "deletion/del".
>Object appears of dissapears in UAPI, no matter how this is implemented
>in kernel. If you call it register/unregister, that exposes unnecessary
>internal kernel notation.
>
>No strong feeling though if you insist on register/unregister, it just
>sounds odd and funny.
>
>Anyway, one way or another, be in-sync naming wise with device events.
>

Sure going in sync with device event names seems best option, will fix as
suggested:
- pin-create
- pin-delete
- pin-change

>
>
>>
>>>
>>>>+        doc: |
>>>>+          attribute of dpll device or pin changed, reason is to be
>found
>>>>with
>>>>+          an attribute type (DPLL_A_*) received with the event
>>>>+
>>>>+
>>>>+attribute-sets:
>>>>+  -
>>>>+    name: dpll
>>>>+    enum-name: dplla
>>>>+    attributes:
>>>>+      -
>>>>+        name: device
>>>>+        type: nest
>>>>+        value: 1
>>>
>>>Why not 0?
>>>
>>
>>Sorry I don't recall what exact technical reasons are behind it, but all
>>netlink attributes I have found have 0 value attribute unused/unspec.
>
>I don't see why that is needed, I may be missing something though.
>Up to you.
>

Will leave it as is, if no other comments.

[...]

>>>>+      attribute-set: dpll
>>>>+      flags: [ admin-perm ]
>>>
>>>I may be missing something, but why do you enforce adming perm for
>>>get/dump cmds?
>>>
>>
>>Yes, security reasons, we don't want regular users to spam-query the driver
>>ops. Also explained in docs:
>>All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
>>any spamming/D.o.S. from unauthorized userspace applications.
>
>Hmm, I wonder why other read cmds usually don't need this. In fact,
>is there some read netlink cmd in kernel now which needs it?
>

Seems drivers/net/team/team.c uses it for get, but anyway this is a "least
privilege" security principle, if regular user cannot do anything with that
information, there is no point on providing it.

>
>>
>>>
>>>>+
>>>>+      do:
>>>>+        pre: dpll-pre-doit
>>>>+        post: dpll-post-doit
>>>>+        request:
>>>>+          attributes:
>>>>+            - id
>>>>+            - bus-name
>>>>+            - dev-name
>>>>+        reply:
>>>>+          attributes:
>>>>+            - device
>>>>+
>>>>+      dump:
>>>>+        pre: dpll-pre-dumpit
>>>>+        post: dpll-post-dumpit
>>>>+        reply:
>>>>+          attributes:
>>>>+            - device
>>>
>>>I might be missing something, but this means "device" netdev attribute
>>>DPLL_A_DEVICE, right? If yes, that is incorrect and you should list all
>>>the device attrs.
>>>
>>
>>Actually this means that attributes expected in response to this command are
>>from `device` subset.
>>But I see your point, will make `device` subset only for pin's nested
>>attributes, and here will list device attributes.
>
>Yes, that is my point. The fix you describes sounds fine.
>
>

Thank you!
Arkadiusz

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11 15:20         ` Jakub Kicinski
@ 2023-05-11 20:53           ` Kubalewski, Arkadiusz
  2023-05-11 23:29             ` Jakub Kicinski
  0 siblings, 1 reply; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11 20:53 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 11, 2023 5:21 PM
>
>On Thu, 11 May 2023 07:40:26 +0000 Kubalewski, Arkadiusz wrote:
>> >> Remove "no holdover available". This is not a state, this is a mode
>> >> configuration. If holdover is or isn't available, is a runtime info.
>> >
>> >Agreed, seems a little confusing now. Should we expose the system clk
>> >as a pin to be able to force lock to it? Or there's some extra magic
>> >at play here?
>>
>> In freerun you cannot lock to anything it, it just uses system clock from
>> one of designated chip wires (which is not a part of source pins pool) to
>> feed
>> the dpll. Dpll would only stabilize that signal and pass it further.
>> Locking itself is some kind of magic, as it usually takes at least ~15
>> seconds
>> before it locks to a signal once it is selected.
>
>Okay, I guess that makes sense.
>
>I was wondering if there may be a DPLLs which allow other input clocks
>to bypass the PLL logic, and output purely a stabilized signal. In
>which case we should model this as a generic PLL bypass, FREERUN being
>just one special case where we're bypassing with the system clock.
>
>But that may well be a case of "software guy thinking", so if nobody
>thinks this can happen in practice we can keep FREERUN.
>

Well I am not saying such use-case doesn't exist, but haven't heard about it.

>> >Noob question, what is NCO in terms of implementation?
>> >We source the signal from an arbitrary pin and FW / driver does
>> >the control? Or we always use system refclk and then tune?
>> >
>>
>> Documentation of chip we are using, stated NCO as similar to FREERUN, and it
>> runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>> dividers before it reaches the output).
>> It doesn't count as an source pin, it uses signal form dedicated wire for
>> SYSTEM CLOCK.
>> In this case control over output frequency is done by synchronizer chip
>> firmware, but still it will not lock to any source pin signal.
>
>Reading wikipedia it sounds like NCO is just a way of generating
>a waveform from synchronous logic.
>
>Does the DPLL not allow changing clock frequency when locked?
>I.e. feeding it one frequency and outputting another?

Well our dpll (actually synchronizer chip) does that in AUTOMATIC/MANUAL modes,
i.e. you feed 1 PPS from gnss and output feed for PHY's ~156 MHZ, so I guess
this is pretty common for complex synchronizer chips, although AFAIK this is
achieved with additional signal synthesizers after the PLL logic.

>Because I think that'd be done by an NCO, no?

From docs I can also see that chip has additional designated dpll for NCO mode,
and this statement:
"Numerically controlled oscillator (NCO) behavior allows system software to steer
DPLL frequency or synthesizer frequency with resolution better than 0.005 ppt."

I am certainly not an expert on this, but seems like the NCO mode for this chip
is better than FREERUN, since signal produced on output is somehow higher quality.

>
>> >> Is it needed to mention the holdover mode. It's slightly confusing,
>> >> because user might understand that the lock-status is always "holdover"
>> >> in case of "holdover" mode. But it could be "unlocked", can't it?
>> >> Perhaps I don't understand the flows there correctly :/
>> >
>> >Hm, if we want to make sure that holdover mode must result in holdover
>> >state then we need some extra atomicity requirements on the SET
>> >operation. To me it seems logical enough that after setting holdover
>> >mode we'll end up either in holdover or unlocked status, depending on
>> >lock status when request reached the HW.
>> >
>>
>> Improved the docs:
>>         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 it was not, the dpll's lock-status will remain
>>           DPLL_LOCK_STATUS_UNLOCKED even if user requests
>>           DPLL_MODE_HOLDOVER)
>> Is that better?
>
>Yes, modulo breaking it up into sentences, as Jiri says.
>

Sure, will do.

>> What extra atomicity you have on your mind?
>> Do you suggest to validate and allow (in dpll_netlink.c) only for 'unlocked'
>> or 'holdover' states of dpll, once DPLL_MODE_HOLDOVER was successfully
>> requested by the user?
>
>No, I was saying that making sure that we end up in holdover (rather
>than unlocked) when user requested holdover is hard, and we shouldn't
>even try to implement that.

Okay.

Thank you!
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11 15:26       ` Jakub Kicinski
@ 2023-05-11 20:54         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-11 20:54 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Jiri Pirko, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 11, 2023 5:27 PM
>
>On Thu, 11 May 2023 07:38:04 +0000 Kubalewski, Arkadiusz wrote:
>> >>+  -
>> >>+    type: enum
>> >>+    name: event
>> >>+    doc: events of dpll generic netlink family
>> >>+    entries:
>> >>+      -
>> >>+        name: unspec
>> >>+        doc: invalid event type
>> >>+      -
>> >>+        name: device-create
>> >>+        doc: dpll device created
>> >>+      -
>> >>+        name: device-delete
>> >>+        doc: dpll device deleted
>> >>+      -
>> >>+        name: device-change
>> >
>> >Please have a separate create/delete/change values for pins.
>> >
>>
>> Makes sense, but details, pin creation doesn't occur from uAPI perspective,
>> as the pins itself are not visible to the user. They are visible after they
>> are registered with a device, thus we would have to do something like:
>> - pin-register
>> - pin-unregister
>> - pin-change
>>
>> Does it make sense?
>
>I missed this, notifications should be declared under operations.
>
>Please look at netdev.yaml for an example.
>
>I thought about implementing this model where events are separate
>explicitly but I think it's an unnecessary complication.

Ok, will do.

Thank you!
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11 20:53           ` Kubalewski, Arkadiusz
@ 2023-05-11 23:29             ` Jakub Kicinski
  2023-05-16 12:15                 ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 149+ messages in thread
From: Jakub Kicinski @ 2023-05-11 23:29 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko

On Thu, 11 May 2023 20:53:40 +0000 Kubalewski, Arkadiusz wrote:
> >Because I think that'd be done by an NCO, no?  
> 
> From docs I can also see that chip has additional designated dpll for NCO mode,
> and this statement:
> "Numerically controlled oscillator (NCO) behavior allows system software to steer
> DPLL frequency or synthesizer frequency with resolution better than 0.005 ppt."
> 
> I am certainly not an expert on this, but seems like the NCO mode for this chip
> is better than FREERUN, since signal produced on output is somehow higher quality.

Herm, this seems complicated. Do you have a use case for this? 
Maybe we can skip it :D

My reading of the quote is that there is an NCO which SW can control
precisely. But that does not answer the questions:
 - is the NCO driven by system clock or can it be used in locked mode?
 - what is the "system software"? FW which based on temperature
   information, and whatever else compensates drift of system clock?
   or there are exposed registers to control the NCO?


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11 20:51           ` Kubalewski, Arkadiusz
@ 2023-05-15  9:30               ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-15  9:30 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, May 11, 2023 10:00 AM
>>
>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>
>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>> >+definitions:
>>>>> >+  -
>>>>> >+    type: enum
>>>>> >+    name: mode
>>>>> >+    doc: |
>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>selects
>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>DPLL_A_MODE
>>>>> >+      attribute
>>>>> >+    entries:
>>>>> >+      -
>>>>> >+        name: unspec
>>>>>
>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>> What is the usecase. If there isn't please remove.
>>>>
>>>>+1
>>>>
>>>
>>>Sure, fixed.
>>>
>>>>> >+        doc: unspecified value
>>>>> >+      -
>>>>> >+        name: manual
>>>>
>>>>I think the documentation calls this "forced", still.
>>>>
>>>
>>>Yes, good catch, fixed docs.
>>>
>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>> >+      -
>>>>> >+        name: automatic
>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>> >+      -
>>>>> >+        name: holdover
>>>>> >+        doc: dpll forced into holdover mode
>>>>> >+      -
>>>>> >+        name: freerun
>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>
>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>
>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>at play here?
>>>
>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>one of designated chip wires (which is not a part of source pins pool) to
>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>seconds before it locks to a signal once it is selected.
>>>
>>>>
>>>>> >+      -
>>>>> >+        name: nco
>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>
>>>>Noob question, what is NCO in terms of implementation?
>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>the control? Or we always use system refclk and then tune?
>>>>
>>>
>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>it
>>
>>So how exactly this is different to freerun? Does user care or he would
>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>device specific thing that should be abstracted out here?
>>
>
>Sure, it is device specific, some synchronizing circuits would have this
>capability, while others would not.
>Should be abstracted out? It is a good question.. shall user know that he is in
>freerun with possibility to control the frequency or not?
>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>supporting multiple output frequencies.
>How the one would know if those frequencies are supported only in
>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>In other words: As the user can I change a frequency of a dpll if active
>mode is FREERUN?

Okay, I think I'm deep in the DPLL infra you are pushing, but my
understanding that you can control frequency in NCO mode is not present
:/ That only means it may be confusing and not described properly.
How do you control this frequency exactly? I see no such knob.

Can't the oscilator be modeled as a pin and then you are not in freerun
but locked this "internal pin"? We know how to control frequency there.


>
>I would say it is better to have such mode, we could argue on naming though.
>
>>
>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>dividers before it reaches the output).
>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>SYSTEM CLOCK.
>>>In this case control over output frequency is done by synchronizer chip
>>>firmware, but still it will not lock to any source pin signal.
>>>
>>>>> >+    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: unspec
>>>>> >+        doc: unspecified value
>>>>> >+      -
>>>>> >+        name: unlocked
>>>>> >+        doc: |
>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>> >+      -
>>>>> >+        name: calibrating
>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>> >+      -
>>>>> >+        name: locked
>>>>> >+        doc: dpll is locked
>>>>> >+      -
>>>>> >+        name: holdover
>>>>> >+        doc: |
>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>
>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>> because user might understand that the lock-status is always "holdover"
>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>> Perhaps I don't understand the flows there correctly :/
>>>>
>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>state then we need some extra atomicity requirements on the SET
>>>>operation. To me it seems logical enough that after setting holdover
>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>lock status when request reached the HW.
>>>>
>>>
>>>Improved the docs:
>>>        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 it was not, the dpll's lock-status will remain
>>
>>"if it was not" does not really cope with the sentence above that. Could
>>you iron-out the phrasing a bit please?
>
>
>Hmmm,
>        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)
>
>Hope this is better?

Okay.

>
>
>Thank you!
>Arkadiusz
>
>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-15  9:30               ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-15  9:30 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, May 11, 2023 10:00 AM
>>
>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>
>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>> >+definitions:
>>>>> >+  -
>>>>> >+    type: enum
>>>>> >+    name: mode
>>>>> >+    doc: |
>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>selects
>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>DPLL_A_MODE
>>>>> >+      attribute
>>>>> >+    entries:
>>>>> >+      -
>>>>> >+        name: unspec
>>>>>
>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>> What is the usecase. If there isn't please remove.
>>>>
>>>>+1
>>>>
>>>
>>>Sure, fixed.
>>>
>>>>> >+        doc: unspecified value
>>>>> >+      -
>>>>> >+        name: manual
>>>>
>>>>I think the documentation calls this "forced", still.
>>>>
>>>
>>>Yes, good catch, fixed docs.
>>>
>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>> >+      -
>>>>> >+        name: automatic
>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>> >+      -
>>>>> >+        name: holdover
>>>>> >+        doc: dpll forced into holdover mode
>>>>> >+      -
>>>>> >+        name: freerun
>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>
>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>
>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>at play here?
>>>
>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>one of designated chip wires (which is not a part of source pins pool) to
>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>seconds before it locks to a signal once it is selected.
>>>
>>>>
>>>>> >+      -
>>>>> >+        name: nco
>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>
>>>>Noob question, what is NCO in terms of implementation?
>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>the control? Or we always use system refclk and then tune?
>>>>
>>>
>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>it
>>
>>So how exactly this is different to freerun? Does user care or he would
>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>device specific thing that should be abstracted out here?
>>
>
>Sure, it is device specific, some synchronizing circuits would have this
>capability, while others would not.
>Should be abstracted out? It is a good question.. shall user know that he is in
>freerun with possibility to control the frequency or not?
>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>supporting multiple output frequencies.
>How the one would know if those frequencies are supported only in
>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>In other words: As the user can I change a frequency of a dpll if active
>mode is FREERUN?

Okay, I think I'm deep in the DPLL infra you are pushing, but my
understanding that you can control frequency in NCO mode is not present
:/ That only means it may be confusing and not described properly.
How do you control this frequency exactly? I see no such knob.

Can't the oscilator be modeled as a pin and then you are not in freerun
but locked this "internal pin"? We know how to control frequency there.


>
>I would say it is better to have such mode, we could argue on naming though.
>
>>
>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>dividers before it reaches the output).
>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>SYSTEM CLOCK.
>>>In this case control over output frequency is done by synchronizer chip
>>>firmware, but still it will not lock to any source pin signal.
>>>
>>>>> >+    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: unspec
>>>>> >+        doc: unspecified value
>>>>> >+      -
>>>>> >+        name: unlocked
>>>>> >+        doc: |
>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>> >+      -
>>>>> >+        name: calibrating
>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>> >+      -
>>>>> >+        name: locked
>>>>> >+        doc: dpll is locked
>>>>> >+      -
>>>>> >+        name: holdover
>>>>> >+        doc: |
>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>
>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>> because user might understand that the lock-status is always "holdover"
>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>> Perhaps I don't understand the flows there correctly :/
>>>>
>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>state then we need some extra atomicity requirements on the SET
>>>>operation. To me it seems logical enough that after setting holdover
>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>lock status when request reached the HW.
>>>>
>>>
>>>Improved the docs:
>>>        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 it was not, the dpll's lock-status will remain
>>
>>"if it was not" does not really cope with the sentence above that. Could
>>you iron-out the phrasing a bit please?
>
>
>Hmmm,
>        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)
>
>Hope this is better?

Okay.

>
>
>Thank you!
>Arkadiusz
>
>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-15 17:12     ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-15 17:12 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:

[...]

>+static const enum dpll_lock_status
>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,

This is a bit confusing to me. You are locked, yet you report
calibrating? Wouldn't it be better to have:
DPLL_LOCK_STATUS_LOCKED
DPLL_LOCK_STATUS_LOCKED_HO_ACQ

?


>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>+};

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-15 17:12     ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-15 17:12 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk

Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:

[...]

>+static const enum dpll_lock_status
>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,

This is a bit confusing to me. You are locked, yet you report
calibrating? Wouldn't it be better to have:
DPLL_LOCK_STATUS_LOCKED
DPLL_LOCK_STATUS_LOCKED_HO_ACQ

?


>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>+};

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-03 12:18     ` Jiri Pirko
@ 2023-05-15 22:07       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-15 22:07 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 3, 2023 2:19 PM
>
>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>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 <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>---
>> 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   | 1929 +++++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
>> drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
>> 8 files changed, 2125 insertions(+), 89 deletions(-)  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 5d89392f969b..6c198cd92d49 100644
>>--- a/drivers/net/ethernet/intel/ice/Makefile
>>+++ b/drivers/net/ethernet/intel/ice/Makefile
>>@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
>>--- a/drivers/net/ethernet/intel/ice/ice.h
>>+++ b/drivers/net/ethernet/intel/ice/ice.h
>>@@ -74,6 +74,7 @@
>> #include "ice_lag.h"
>> #include "ice_vsi_vlan_ops.h"
>> #include "ice_gnss.h"
>>+#include "ice_dpll.h"
>>
>> #define ICE_BAR0		0
>> #define ICE_REQ_DESC_MULTIPLE	32
>>@@ -201,6 +202,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,
>>@@ -512,6 +514,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 */
>> };
>>
>>@@ -635,6 +638,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..3217fb36dd12
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>@@ -0,0 +1,1929 @@
>>+// 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>
>>+#include <uapi/linux/dpll.h>
>
>Don't include uapi directly. I'm pretty sure I had the same comment the
>last time as well.
>

Fixed, no idea how it is still there, thanks!

>
>>+
>>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
>>+#define ICE_DPLL_LOCK_TRIES		1000
>>+#define ICE_DPLL_PIN_IDX_INVALID	0xff
>>+
>>+/**
>>+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[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_SOURCE,
>>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>>+};
>>+
>>+/**
>>+ * pin_type_name - string names of ice pin types  */ static const char
>>+* const pin_type_name[] = {
>>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source", };
>>+
>>+/**
>>+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin index of a searched dpll subsystem
>>+ * pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid index for a given pin & pin type found on pf internal dpll
>>+struct
>>+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
>>+ */
>>+static u32
>>+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
>>+		 enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>
>How this can happen? If not, remove.
>

Makes sense, fixed.

>
>>+		return ICE_DPLL_PIN_IDX_INVALID;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else {
>>+		return ICE_DPLL_PIN_IDX_INVALID;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return i;
>>+
>>+	return ICE_DPLL_PIN_IDX_INVALID;
>>+}
>>+
>>+/**
>>+ * 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)
>
>On many places you call this without saving the return value to int
>variable. You should do that and propagate the error value.
>

That is true, fixed.

>
>>+{
>>+	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_find_pin - find ice_dpll_pin on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin info pointer holding data of given
>>+dpll
>>+ * subsystem pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer
>>+was
>>+ * found in pf internal pin data.
>>+ * * NULL - if pin was not found.
>>+ */
>>+static struct ice_dpll_pin
>>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>>+	      enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>>+		return NULL;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		if (pin == pf->dplls.rclk.pin)
>>+			return &pf->dplls.rclk;
>>+	} else {
>>+		return NULL;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return &pins[i];
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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,
>>+		      const enum ice_dpll_pin_type pin_type, const u32 freq) {
>>+	u8 flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		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);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>+						0, freq, 0);
>>+	} else {
>
>How exactly this can happen? If not, avoid it.
>And use switch-case for enum values
>

Sure, fixed.

>
>>+		ret = -EINVAL;
>>+	}
>>+
>>+	if (ret) {
>>+		dev_dbg(ice_pf_to_dev(pf),
>
>dev_err
>

True, fixed.

>
>>+			"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);
>>+	} else {
>>+		pin->freq = freq;
>>+	}
>>+
>>+	return ret;
>
>Usual pattern is:
>	ret = something() //switch-case in this case
>	if (ret)
>		return ret;
>	return 0;
>Easier to follow.
>

Ok, fixed.

>
>>+}
>>+
>>+/**
>>+ * 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
>>+ * @frequency: frequency to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of pin being configured
>>+ *
>>+ * Wraps internal set frequency command on a pin.
>>+ *
>>+ * 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,
>>+		       const u32 frequency,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>
>This does not make any sense to me. You should avoid the lookups and remove
>ice_find_pin() function entirely. The purpose of having pin_priv is to
>carry the struct ice_dpll_pin * directly. You should pass it down during
>pin register.
>
>pf pointer is stored in dpll_priv.
>

In this case dpll_priv is not passed, so cannot use it.
But in general it makes sense I will hold pf inside of ice_dpll_pin
and fix this.

>
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>
>That would be very odd message the user would see :)
>

Removed.

>
>>+		goto unlock;
>>+	}
>>+
>>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>>+	if (ret)
>>+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);
>
>Why you need to print "ret"? It is propagated to the caller as a return
>value.
>

Removed.

>
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_frequency_set - source 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't set in hw  */ static
>>+int ice_dpll_source_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, (u32)frequency,
>>+extack,
>
>Avoid the cast here, not needed.
>
>The dpll core should do check if user passes frequency which is supported,
>so you don't care about the overflow either.

Fixed.

>
>
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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, 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.
>>+ *
>>+ * 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, u64 *frequency,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	*frequency = (u64)(p->freq);
>
>Drop the pointless cast.
>

Removed.

>
>>+	ret = 0;
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_frequency_get - source 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 source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't get from hw  */ static
>>+int ice_dpll_source_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, frequency, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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, 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.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type) {
>>+	u8 flags = pin->flags[0];
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	}
>
>switch-case
>

Fixed.

>
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>
>dev_err?
>

Fixed.

>
>>+			"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);
>>+	else
>>+		pin->flags[0] = flags;
>>+
>>+	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.
>>+ *
>>+ * 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) {
>>+	u8 flags = pin->flags[0];
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	}
>
>switch-case?
>

Fixed.

>
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>
>dev_err?
>

Fixed.

>
>>+			"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);
>>+	else
>>+		pin->flags[0] = flags;
>>+
>>+	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 source pin states are separated for each
>>+ * dpll, for rclk pins states are separated for each parent.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+int
>>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+			  const enum ice_dpll_pin_type pin_type) {
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		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])) {
>
>Don't do "!!", it's not needed. You have this on multiple places. Please
>reduce.
>

Fixed.

>
>>+			if (pin->pin) {
>>+				pin->state[pf->dplls.eec.dpll_idx] =
>>+					pin->pin == pf->dplls.eec.active_source ?
>>+					DPLL_PIN_STATE_CONNECTED :
>>+					DPLL_PIN_STATE_SELECTABLE;
>>+				pin->state[pf->dplls.pps.dpll_idx] =
>>+					pin->pin == pf->dplls.pps.active_source ?
>>+					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;
>>+		}
>>+	} else if (pin_type == 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;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		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;
>>+		}
>>+	}
>
>Perhaps:
>
>	switch (pin_type) {
>	case ICE_DPLL_PIN_TYPE_SOURCE:
>		..
>	case ICE_DPLL_PIN_TYPE_OUTPUT:
>		..
>
>?

Fixed.

>
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_find_dpll - find ice_dpll on a pf
>>+ * @pf: private board structure
>>+ * @dpll: kernel's dpll_device pointer to be searched
>>+ *
>>+ * 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_source_prio_set - set source 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_hw_source_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_dbg(ice_pf_to_dev(pf),
>
>dev_err
>

Fixed.

>
>>+			"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.
>>+ *
>>+ * 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_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return -EINVAL;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	d = ice_find_dpll(pf, dpll);
>
>Another example of odd and unneeded lookup. Register dpll device with
>struct ice_dpll *d as a priv. Store pf pointer there in struct ice_dpll.
>And remove ice_find_dpll() entirely.
>

Fixed.

>
>
>>+	if (!d)
>>+		return -EFAULT;
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>>+	*status = ice_dpll_status[d->dpll_state];
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * 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) {
>>+	struct ice_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return -EINVAL;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	d = ice_find_dpll(pf, dpll);
>>+	ice_dpll_cb_unlock(pf);
>>+	if (!d)
>>+		return -EFAULT;
>>+	*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,
>>+				    const enum dpll_mode mode,
>>+				    struct netlink_ext_ack *extack) {
>>+	struct ice_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return false;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return false;
>>+	d = ice_find_dpll(pf, dpll);
>>+	ice_dpll_cb_unlock(pf);
>>+	if (!d)
>>+		return false;
>>+	if (mode == DPLL_MODE_AUTOMATIC)
>>+		return true;
>>+
>>+	return false;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>+ * @dpll: dpll being configured
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @state: state of pin to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of a pin
>>+ *
>>+ * Set pin state on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin, void *pin_priv,
>>+		       const enum dpll_pin_state state,
>
>Why you use const with enums?
>

Just show usage intention explicitly.

>
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		if (state == DPLL_PIN_STATE_SELECTABLE)
>>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		if (state == DPLL_PIN_STATE_CONNECTED)
>>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>
>switch-case?
>
>Perhaps it would be nicer to do this in ice_dpll_output_state_set() and
>ice_dpll_source_state_set() directly?
>

Sure makes sense, fixed.

>
>>+	}
>>+	if (!ret)
>>+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+	dev_dbg(ice_pf_to_dev(pf),
>
>dev_err in case ret != 0 ?
>

Fixed.

>
>>+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
>>+		__func__, dpll, pin, p, pf, state, 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.
>>+ *
>>+ * 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,
>>+				     const enum dpll_pin_state state,
>>+				     struct netlink_ext_ack *extack) {
>>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_source_state_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) {
>>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_get - set pin's state on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int
>>+ice_dpll_pin_state_get(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin, void *pin_priv,
>>+		       enum dpll_pin_state *state,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	struct ice_dpll *d;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	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_SOURCE)
>>+		*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);
>>+	dev_dbg(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.
>>+ *
>>+ * 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(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_state_get - get source 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 source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_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_pf *pf = pin_priv;
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		NL_SET_ERR_MSG(extack, "dpll not found");
>>+		goto unlock;
>>+	}
>>+	*prio = d->input_prio[p->idx];
>>+	ret = 0;
>>+unlock:
>>+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_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_pf *pf = pin_priv;
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (prio > ICE_DPLL_PRIO_MAX) {
>>+		NL_SET_ERR_MSG(extack, "prio out of range");
>>+		return ret;
>>+	}
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		NL_SET_ERR_MSG(extack, "dpll not found");
>>+		goto unlock;
>>+	}
>>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>>+	if (ret)
>>+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);
>
>Why you need to print "ret"? It is propagated to the caller as a return
>value.
>

Fixed.

>
>>+unlock:
>>+	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_source_direction - callback for get source 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 source pin direction
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting direction of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ */
>>+static int ice_dpll_source_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_SOURCE;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_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
>>+ *
>>+ * 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,
>>+					  const enum dpll_pin_state state,
>>+					  struct netlink_ext_ack *extack) {
>>+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>
>Again, this does not make sense. You need struct ice_dpll_pin * related to
>parent. That should be parent priv and passed to dpll subsystem during
>registration and put as an "void * parent_pin_priv" arg to
>.state_on_pin_set() op. Whenever you do lookup like this, it is most
>usually wrong.
>

Fixed.

>
>
>>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>
>Can't you just store idx in struct ice_dpll_pin to avoid lookups like this
>one?
>

Fixed.

>
>>+			hw_idx = i;
>>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+
>>+	if ((enable && !!(p->flags[hw_idx] &
>>+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
>>+	    (!enable && !(p->flags[hw_idx] &
>>+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
>>+		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 ret:%d\n",
>>+		__func__, parent_pin, pin, pf, 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>
>I wonder how valuable this return values table is for a reader.
>Not much I suppose. Do you need it in comments to all the functions you
>have here?
>

0 is success - most impoertant. I will leave those as is.

>
>>+ */
>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>+					  void *pin_priv,
>>+					  const struct dpll_pin *parent_pin,
>>+					  enum dpll_pin_state *state,
>>+					  struct netlink_ext_ack *extack) {
>>+	struct ice_pf *pf = pin_priv;
>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>
>Reverse christmas tree ordering please.

Fixed.

>
>
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	if (!pf)
>
>How exacly this can happen. My wild guess is it can't. Don't do such
>pointless checks please, confuses the reader.
>

From driver perspective the pf pointer value is given by external entity,
why shouldn't it be valdiated?

>
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>>+			hw_idx = i;
>>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+
>>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (ret)
>>+		goto unlock;
>>+
>>+	if (!!(p->flags[hw_idx] &
>>+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))
>
>Avoid needless "!!".
>

Fixed.

>
>>+		*state = DPLL_PIN_STATE_CONNECTED;
>>+	else
>>+		*state = DPLL_PIN_STATE_DISCONNECTED;
>
>Use ternary operator perhaps?
>

Fixed.

>
>>+	ret = 0;
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, parent_pin, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>
>const.
>

Fixed.

>
>>+	.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_source_direction, };
>>+
>>+static struct dpll_pin_ops ice_dpll_source_ops = {
>
>const.
>

Fixed.

>
>>+	.frequency_get = ice_dpll_source_frequency_get,
>>+	.frequency_set = ice_dpll_source_frequency_set,
>>+	.state_on_dpll_get = ice_dpll_source_state_get,
>>+	.state_on_dpll_set = ice_dpll_source_state_set,
>>+	.prio_get = ice_dpll_source_prio_get,
>>+	.prio_set = ice_dpll_source_prio_set,
>>+	.direction_get = ice_dpll_source_direction, };
>>+
>>+static struct dpll_pin_ops ice_dpll_output_ops = {
>
>const.
>

Fixed.

>>+	.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 struct dpll_device_ops ice_dpll_ops = {
>
>const.
>

Fixed.

>
>>+	.lock_status_get = ice_dpll_lock_status_get,
>>+	.mode_get = ice_dpll_mode_get,
>>+	.mode_supported = ice_dpll_mode_supported, };
>>+
>>+/**
>>+ * ice_dpll_release_info - release memory allocated for pins
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ */
>>+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
>>+ * @pf: board private structure
>>+ *
>>+ * Deregister from parent pins and release resources in dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_release_rclk_pin(struct ice_pf *pf) {
>>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>>+	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;
>>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>>+					   &ice_dpll_rclk_ops, pf);
>>+	}
>>+	dpll_pin_put(rclk->pin);
>>+	rclk->pin = NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release_pins - release pin's from dplls registered in
>>+subsystem
>>+ * @pf: board private structure
>>+ * @dpll_eec: dpll_eec dpll pointer
>>+ * @dpll_pps: dpll_pps dpll pointer
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Deregister and free pins of a given array of pins from dpll devices
>>+ * registered in dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
>>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>>+		      int count, struct dpll_pin_ops *ops, bool cgu) {
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		struct ice_dpll_pin *p = &pins[i];
>>+
>>+		if (p && !IS_ERR_OR_NULL(p->pin)) {
>>+			if (cgu && dpll_eec)
>>+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
>>+			if (cgu && dpll_pps)
>>+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
>>+			dpll_pin_put(p->pin);
>>+			p->pin = NULL;
>>+		}
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Register source or output pins within given DPLL in a Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu) {
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	u32 rclk_idx;
>>+	int ret, i;
>>+
>>+	ops = &ice_dpll_source_ops;
>>+	pins = pf->dplls.inputs;
>>+	for (i = 0; i < pf->dplls.num_inputs; i++) {
>>+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
>>+					   THIS_MODULE, &pins[i].prop);
>>+		if (IS_ERR_OR_NULL(pins[i].pin)) {
>
>How exactly dpll_pin_get() can return NULL? It can't, use IS_ERR.
>Same in ice_dpll_release_pins() and two occurances below.
>

Fixed.

>
>
>>+			pins[i].pin = NULL;
>>+			return -ENOMEM;
>>+		}
>>+		if (cgu) {
>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>+						pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>+						pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>
>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>here.
>

No, in case of error, the caller releases everything ice_dpll_release_all(..).

>
>>+		}
>>+	}
>>+	if (cgu) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>+						   i + pf->dplls.num_inputs,
>>+						   THIS_MODULE, &pins[i].prop);
>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>+				pins[i].pin = NULL;
>>+				return -ENOMEM;
>
>Don't make up error values when you get them from the function you call:
>	return PTR_ERR(pins[i].pin);

Fixed.

>
>>+			}
>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>
>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>here.
>

As above, in case of error, the caller releases everything.

>
>>+                                              ops, pf, NULL);
>
>
>>+		}
>>+	}
>>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf-
>>hw.pf_id;
>>+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
>>+					  THIS_MODULE, &pf->dplls.rclk.prop);
>>+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
>>+		pf->dplls.rclk.pin = NULL;
>>+		return -ENOMEM;
>
>Don't make up error values when you get them from the function you call:
>	return PTR_ERR(pf->dplls.rclk.pin);
>

Fixed.

>
>>+	}
>>+	ops = &ice_dpll_rclk_ops;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>>+		struct dpll_pin *parent =
>>+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>>+
>>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>>+					       ops, pf, dev);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>>+ * @pf: board private structure
>>+ * @clock_id: holds generated clock_id
>>+ *
>>+ * Generates unique (per board) clock_id for allocation and search of
>>+dpll
>>+ * devices in Linux dpll subsystem.
>>+ */
>>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id) {
>>+	*clock_id = pci_get_dsn(pf->pdev);
>>+}
>
>How about:
>
>static u64 ice_generate_clock_id(struct ice_pf *pf) {
>	return pci_get_dsn(pf->pdev);
>}
>
>??
>

Fixed.

>
>>+
>>+/**
>>+ * ice_dpll_init_dplls
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Get dplls instances for this board, if cgu is controlled by this
>>+NIC,
>>+ * register dpll with callbacks ops
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation fails
>>+ */
>>+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu) {
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	int ret = -ENOMEM;
>>+	u64 clock_id;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf-
>>dplls.eec.dpll_idx,
>>+					     THIS_MODULE);
>>+	if (!pf->dplls.eec.dpll) {
>
>You have to use IS_ERR()
>

Fixed.

>
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
>>+		return ret;
>>+	}
>>+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf-
>>dplls.pps.dpll_idx,
>>+					     THIS_MODULE);
>>+	if (!pf->dplls.pps.dpll) {
>
>You have to use IS_ERR()
>

Fixed.

>
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
>>+		goto put_eec;
>>+	}
>>+
>>+	if (cgu) {
>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>+					   &ice_dpll_ops, pf, dev);
>>+		if (ret)
>>+			goto put_pps;
>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>+					   &ice_dpll_ops, pf, dev);
>>+		if (ret)
>
>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>DPLL_TYPE_EEC here. Fix the error path.
>

The caller shall do the clean up, but yeah will fix this as here clean up
is not expected.

>
>>+			goto put_pps;
>>+	}
>>+
>>+	return 0;
>>+
>>+put_pps:
>>+	dpll_device_put(pf->dplls.pps.dpll);
>>+	pf->dplls.pps.dpll = NULL;
>>+put_eec:
>>+	dpll_device_put(pf->dplls.eec.dpll);
>>+	pf->dplls.eec.dpll = NULL;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * 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.
>>+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		return ice_dpll_pin_state_update(pf, p,
>>+						 ICE_DPLL_PIN_TYPE_SOURCE);
>>+	}
>>+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>>+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
>>+		d->active_source = NULL;
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+		ret = ice_dpll_pin_state_update(pf, p,
>>ICE_DPLL_PIN_TYPE_SOURCE);
>>+	} else if (d->source_idx != d->prev_source_idx) {
>>+		p = &pf->dplls.inputs[d->prev_source_idx];
>>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		d->active_source = p->pin;
>>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>>+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
>>+	}
>>+	if (d->prev_source != d->active_source) {
>>+		d->prev_source = d->active_source;
>>+		if (d->active_source)
>>+			dpll_pin_notify(d->dpll, d->active_source,
>>+					DPLL_A_PIN_STATE);
>
>Didn't the state of the previously active source change as well? You need
>to send notification for that too.
>

Makes sense, fixed.

>
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>+ * @work: pointer to kthread_work structure
>>+ *
>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>+ */
>>+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.
>>+ *
>>+ * 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_release_all - disable support for DPLL and unregister dpll
>>+device
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is controlled by this driver instance
>>+ *
>>+ * This function handles the cleanup work required from the
>>+initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ *
>>+ * Context: Called under pf->dplls.lock  */ static void
>>+ice_dpll_release_all(struct ice_pf *pf, bool cgu) {
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	ice_dpll_release_rclk_pin(pf);
>>+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
>>+			      d->num_inputs, &ice_dpll_source_ops, cgu);
>>+	mutex_unlock(&pf->dplls.lock);
>>+	if (cgu) {
>>+		mutex_lock(&pf->dplls.lock);
>
>Interesting, you lock again a lock you just unlocked. One might wonder why
>you just don't move the call to mutex_unlock below this "if section".
>

Fixed.

>
>>+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
>>+				      d->num_outputs,
>>+				      &ice_dpll_output_ops, cgu);
>>+		mutex_unlock(&pf->dplls.lock);
>>+	}
>>+	ice_dpll_release_info(pf);
>>+	if (dp->dpll) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		if (cgu)
>>+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
>>+		dpll_device_put(dp->dpll);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>>+	}
>>+
>>+	if (de->dpll) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		if (cgu)
>>+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
>>+		dpll_device_put(de->dpll);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>>+	}
>>+
>>+	if (cgu) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		kthread_cancel_delayed_work_sync(&d->work);
>>+		if (d->kworker) {
>>+			kthread_destroy_worker(d->kworker);
>>+			d->kworker = NULL;
>>+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>>+		}
>>+		mutex_unlock(&pf->dplls.lock);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and
>>+unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * Handles the cleanup work required after dpll initialization,
>>+ * freeing resources and unregistering the dpll.
>>+ */
>>+void ice_dpll_release(struct ice_pf *pf) {
>>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		ice_dpll_release_all(pf,
>>+				     ice_is_feature_supported(pf, ICE_F_CGU));
>>+		mutex_destroy(&pf->dplls.lock);
>>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_direct_pins - initializes source or output pins
>>+information
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information about input or output pins, cache them in pins struct.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int
>>+ice_dpll_init_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;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+		input = true;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+		input = false;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].prop.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;
>
>It is a flag. Common is to use bit op &= instead.
>

Fixed.

>
>>+		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;
>>+	}
>>+
>>+	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
>>+ */
>>+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.label = dev_name(dev);
>>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+
>>+	return ice_dpll_pin_state_update(pf, pin,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - init pins wrapper
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Wraps functions for pin inti.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf,
>>+			      const enum ice_dpll_pin_type pin_type) {
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
>>+		return ice_dpll_init_direct_pins(pf, pin_type);
>>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>>+		return ice_dpll_init_direct_pins(pf, pin_type);
>>+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
>>+		return ice_dpll_init_rclk_pin(pf);
>>+	else
>>+		return -EINVAL;
>
>How this can happen?
>
>How about:
>	switch (pin_type) {
>	case ICE_DPLL_PIN_TYPE_SOURCE:
>	case ICE_DPLL_PIN_TYPE_OUTPUT:
>		return ice_dpll_init_direct_pins(pf, pin_type);
>	case ICE_DPLL_PIN_TYPE_RCLK_SOURCE:
>		return ice_dpll_init_rclk_pin(pf);
>	}
>?
>

It shall not if called properly, changed to switch case as suggested.
Altough still needs default: as there is one not covered case, thus compiler
warns about it.

>
>
>>+}
>>+
>>+/**
>>+ * 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 - error
>>+ */
>>+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;
>>+	u8 base_rclk_idx;
>>+
>>+	ice_generate_clock_id(pf, &d->clock_id);
>>+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	if (cgu) {
>>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+		if (!d->outputs)
>>+			goto release_info;
>>+
>>+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+		if (ret)
>>+			goto release_info;
>>+	}
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	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;
>>+
>>+release_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_release_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - initialize dplls support
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device dplls registering 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
>>+ */
>>+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 release;
>>+	err = ice_dpll_init_dplls(pf, cgu_present);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, cgu_present);
>
>This should be rather called "ice_dpll_init_pins()" to be in sync with
>ice_dpll_init_dplls() as it is doing more then just registration.
>

Makes sense, fixed.

Thank you very much!
Arkadiusz

>
>>+	if (err)
>>+		goto release;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	if (cgu_present) {
>>+		err = ice_dpll_init_worker(pf);
>>+		if (err)
>>+			goto release;
>>+	}
>>+	mutex_unlock(&d->lock);
>>+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>>+
>>+	return err;
>>+
>>+release:
>>+	ice_dpll_release_all(pf, cgu_present);
>>+	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\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..aad48b9910b7
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,101 @@
>>+/* 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
>>+ * @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;
>>+	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
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @source_idx: source currently selected
>>+ * @prev_source_idx: source previously selected
>>+ * @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_source: pointer to active source pin
>>+ * @prev_source: pointer to previous active source pin  */ struct
>>+ice_dpll {
>>+	struct dpll_device *dpll;
>>+	int dpll_idx;
>>+	u8 source_idx;
>>+	u8 prev_source_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_source;
>>+	struct dpll_pin *prev_source;
>>+};
>>+
>>+/** 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
>>+ * @num_rclk: number of recovered clock pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic
>>+work  */ 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_release(struct ice_pf *pf);
>>+
>>+int ice_dpll_rclk_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4821,6 +4821,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"); @@ -
>4847,6
>>+4851,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_release(pf);
>> }
>>
>> static void ice_init_wakeup(struct ice_pf *pf) diff --git
>>a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>index e9a371fa038b..39b692945f73 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct
>>ice_hw *hw, u8 pin, bool input)  }
>>
>> /**
>>- * ice_cgu_get_pin_sig_type_mask
>>+ * 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
>>  *
>>- * Return: signal type bit mask of a pin.
>>+ * Get frequency supported number and array of supported frequencies.
>>+ *
>>+ * Return: array of supported frequencies for given pin.
>>  */
>>-unsigned long
>>-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
>> 	int t_size;
>>
>>+	*num = 0;
>> 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
>>-
>> 	if (!t)
>>-		return 0;
>>-
>>+		return NULL;
>> 	if (pin >= t_size)
>>-		return 0;
>>+		return NULL;
>>+	*num = t[pin].freq_supp_num;
>>
>>-	return t[pin].sig_type_mask;
>>+	return t[pin].freq_supp;
>> }
>>
>> /**
>>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>index d09e5bca0ff1..4568b0403cd7 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
>> 	NUM_SI_CGU_OUTPUT_PINS
>> };
>>
>>-#define MAX_CGU_PIN_NAME_LEN		16
>>-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
>>-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
>>+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[MAX_CGU_PIN_NAME_LEN];
>>+	char *name;
>> 	u8 index;
>> 	enum dpll_pin_type type;
>>-	unsigned long sig_type_mask;
>>+	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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
>>-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+	{ "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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
>>-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
>> };
>>
>>@@ -429,8 +419,8 @@ 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); -unsigned
>>long -ice_cgu_get_pin_freq_mask(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,
>>--
>>2.34.1
>>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-15 22:07       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-15 22:07 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 3, 2023 2:19 PM
>
>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>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 <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>---
>> 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   | 1929 +++++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h   |  101 +
>> drivers/net/ethernet/intel/ice/ice_main.c   |    7 +
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   21 +-
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h |  148 +-
>> 8 files changed, 2125 insertions(+), 89 deletions(-)  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 5d89392f969b..6c198cd92d49 100644
>>--- a/drivers/net/ethernet/intel/ice/Makefile
>>+++ b/drivers/net/ethernet/intel/ice/Makefile
>>@@ -33,7 +33,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 5736757039db..a71d46e41c01 100644
>>--- a/drivers/net/ethernet/intel/ice/ice.h
>>+++ b/drivers/net/ethernet/intel/ice/ice.h
>>@@ -74,6 +74,7 @@
>> #include "ice_lag.h"
>> #include "ice_vsi_vlan_ops.h"
>> #include "ice_gnss.h"
>>+#include "ice_dpll.h"
>>
>> #define ICE_BAR0		0
>> #define ICE_REQ_DESC_MULTIPLE	32
>>@@ -201,6 +202,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,
>>@@ -512,6 +514,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 */
>> };
>>
>>@@ -635,6 +638,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..3217fb36dd12
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>@@ -0,0 +1,1929 @@
>>+// 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>
>>+#include <uapi/linux/dpll.h>
>
>Don't include uapi directly. I'm pretty sure I had the same comment the
>last time as well.
>

Fixed, no idea how it is still there, thanks!

>
>>+
>>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD	50
>>+#define ICE_DPLL_LOCK_TRIES		1000
>>+#define ICE_DPLL_PIN_IDX_INVALID	0xff
>>+
>>+/**
>>+ * 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] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[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_SOURCE,
>>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>>+};
>>+
>>+/**
>>+ * pin_type_name - string names of ice pin types  */ static const char
>>+* const pin_type_name[] = {
>>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source", };
>>+
>>+/**
>>+ * ice_find_pin_idx - find ice_dpll_pin index on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin index of a searched dpll subsystem
>>+ * pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid index for a given pin & pin type found on pf internal dpll
>>+struct
>>+ * * ICE_DPLL_PIN_IDX_INVALID - if pin was not found.
>>+ */
>>+static u32
>>+ice_find_pin_idx(struct ice_pf *pf, const struct dpll_pin *pin,
>>+		 enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>
>How this can happen? If not, remove.
>

Makes sense, fixed.

>
>>+		return ICE_DPLL_PIN_IDX_INVALID;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else {
>>+		return ICE_DPLL_PIN_IDX_INVALID;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return i;
>>+
>>+	return ICE_DPLL_PIN_IDX_INVALID;
>>+}
>>+
>>+/**
>>+ * 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)
>
>On many places you call this without saving the return value to int
>variable. You should do that and propagate the error value.
>

That is true, fixed.

>
>>+{
>>+	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_find_pin - find ice_dpll_pin on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin info pointer holding data of given
>>+dpll
>>+ * subsystem pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer
>>+was
>>+ * found in pf internal pin data.
>>+ * * NULL - if pin was not found.
>>+ */
>>+static struct ice_dpll_pin
>>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>>+	      enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>>+		return NULL;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		if (pin == pf->dplls.rclk.pin)
>>+			return &pf->dplls.rclk;
>>+	} else {
>>+		return NULL;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return &pins[i];
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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,
>>+		      const enum ice_dpll_pin_type pin_type, const u32 freq) {
>>+	u8 flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		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);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags = pin->flags[0] | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>+						0, freq, 0);
>>+	} else {
>
>How exactly this can happen? If not, avoid it.
>And use switch-case for enum values
>

Sure, fixed.

>
>>+		ret = -EINVAL;
>>+	}
>>+
>>+	if (ret) {
>>+		dev_dbg(ice_pf_to_dev(pf),
>
>dev_err
>

True, fixed.

>
>>+			"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);
>>+	} else {
>>+		pin->freq = freq;
>>+	}
>>+
>>+	return ret;
>
>Usual pattern is:
>	ret = something() //switch-case in this case
>	if (ret)
>		return ret;
>	return 0;
>Easier to follow.
>

Ok, fixed.

>
>>+}
>>+
>>+/**
>>+ * 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
>>+ * @frequency: frequency to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of pin being configured
>>+ *
>>+ * Wraps internal set frequency command on a pin.
>>+ *
>>+ * 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,
>>+		       const u32 frequency,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>
>This does not make any sense to me. You should avoid the lookups and remove
>ice_find_pin() function entirely. The purpose of having pin_priv is to
>carry the struct ice_dpll_pin * directly. You should pass it down during
>pin register.
>
>pf pointer is stored in dpll_priv.
>

In this case dpll_priv is not passed, so cannot use it.
But in general it makes sense I will hold pf inside of ice_dpll_pin
and fix this.

>
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>
>That would be very odd message the user would see :)
>

Removed.

>
>>+		goto unlock;
>>+	}
>>+
>>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>>+	if (ret)
>>+		NL_SET_ERR_MSG_FMT(extack, "freq not set, err:%d", ret);
>
>Why you need to print "ret"? It is propagated to the caller as a return
>value.
>

Removed.

>
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_frequency_set - source 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't set in hw  */ static
>>+int ice_dpll_source_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, (u32)frequency,
>>+extack,
>
>Avoid the cast here, not needed.
>
>The dpll core should do check if user passes frequency which is supported,
>so you don't care about the overflow either.

Fixed.

>
>
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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, 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.
>>+ *
>>+ * 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, u64 *frequency,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	*frequency = (u64)(p->freq);
>
>Drop the pointless cast.
>

Removed.

>
>>+	ret = 0;
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_frequency_get - source 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 source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't get from hw  */ static
>>+int ice_dpll_source_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, frequency, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * 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.
>>+ *
>>+ * 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, 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.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type) {
>>+	u8 flags = pin->flags[0];
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	}
>
>switch-case
>

Fixed.

>
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>
>dev_err?
>

Fixed.

>
>>+			"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);
>>+	else
>>+		pin->flags[0] = flags;
>>+
>>+	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.
>>+ *
>>+ * 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) {
>>+	u8 flags = pin->flags[0];
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	}
>
>switch-case?
>

Fixed.

>
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>
>dev_err?
>

Fixed.

>
>>+			"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);
>>+	else
>>+		pin->flags[0] = flags;
>>+
>>+	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 source pin states are separated for each
>>+ * dpll, for rclk pins states are separated for each parent.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+int
>>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+			  const enum ice_dpll_pin_type pin_type) {
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		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])) {
>
>Don't do "!!", it's not needed. You have this on multiple places. Please
>reduce.
>

Fixed.

>
>>+			if (pin->pin) {
>>+				pin->state[pf->dplls.eec.dpll_idx] =
>>+					pin->pin == pf->dplls.eec.active_source ?
>>+					DPLL_PIN_STATE_CONNECTED :
>>+					DPLL_PIN_STATE_SELECTABLE;
>>+				pin->state[pf->dplls.pps.dpll_idx] =
>>+					pin->pin == pf->dplls.pps.active_source ?
>>+					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;
>>+		}
>>+	} else if (pin_type == 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;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		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;
>>+		}
>>+	}
>
>Perhaps:
>
>	switch (pin_type) {
>	case ICE_DPLL_PIN_TYPE_SOURCE:
>		..
>	case ICE_DPLL_PIN_TYPE_OUTPUT:
>		..
>
>?

Fixed.

>
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_find_dpll - find ice_dpll on a pf
>>+ * @pf: private board structure
>>+ * @dpll: kernel's dpll_device pointer to be searched
>>+ *
>>+ * 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_source_prio_set - set source 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_hw_source_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_dbg(ice_pf_to_dev(pf),
>
>dev_err
>

Fixed.

>
>>+			"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.
>>+ *
>>+ * 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_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return -EINVAL;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	d = ice_find_dpll(pf, dpll);
>
>Another example of odd and unneeded lookup. Register dpll device with
>struct ice_dpll *d as a priv. Store pf pointer there in struct ice_dpll.
>And remove ice_find_dpll() entirely.
>

Fixed.

>
>
>>+	if (!d)
>>+		return -EFAULT;
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>>+	*status = ice_dpll_status[d->dpll_state];
>>+	ice_dpll_cb_unlock(pf);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * 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) {
>>+	struct ice_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return -EINVAL;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	d = ice_find_dpll(pf, dpll);
>>+	ice_dpll_cb_unlock(pf);
>>+	if (!d)
>>+		return -EFAULT;
>>+	*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,
>>+				    const enum dpll_mode mode,
>>+				    struct netlink_ext_ack *extack) {
>>+	struct ice_pf *pf = priv;
>>+	struct ice_dpll *d;
>>+
>>+	if (!pf)
>>+		return false;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return false;
>>+	d = ice_find_dpll(pf, dpll);
>>+	ice_dpll_cb_unlock(pf);
>>+	if (!d)
>>+		return false;
>>+	if (mode == DPLL_MODE_AUTOMATIC)
>>+		return true;
>>+
>>+	return false;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>+ * @dpll: dpll being configured
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @state: state of pin to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of a pin
>>+ *
>>+ * Set pin state on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin, void *pin_priv,
>>+		       const enum dpll_pin_state state,
>
>Why you use const with enums?
>

Just show usage intention explicitly.

>
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		if (state == DPLL_PIN_STATE_SELECTABLE)
>>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		if (state == DPLL_PIN_STATE_CONNECTED)
>>+			ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>>+		else if (state == DPLL_PIN_STATE_DISCONNECTED)
>>+			ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>
>switch-case?
>
>Perhaps it would be nicer to do this in ice_dpll_output_state_set() and
>ice_dpll_source_state_set() directly?
>

Sure makes sense, fixed.

>
>>+	}
>>+	if (!ret)
>>+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+	dev_dbg(ice_pf_to_dev(pf),
>
>dev_err in case ret != 0 ?
>

Fixed.

>
>>+		"%s: dpll:%p, pin:%p, p:%p pf:%p state: %d ret:%d\n",
>>+		__func__, dpll, pin, p, pf, state, 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.
>>+ *
>>+ * 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,
>>+				     const enum dpll_pin_state state,
>>+				     struct netlink_ext_ack *extack) {
>>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_state_set - enable/disable source 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 source type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_source_state_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) {
>>+	return ice_dpll_pin_state_set(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_get - set pin's state on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int
>>+ice_dpll_pin_state_get(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin, void *pin_priv,
>>+		       enum dpll_pin_state *state,
>>+		       struct netlink_ext_ack *extack,
>>+		       const enum ice_dpll_pin_type pin_type) {
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	struct ice_dpll *d;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	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_SOURCE)
>>+		*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);
>>+	dev_dbg(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.
>>+ *
>>+ * 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(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_state_get - get source 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 source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int ice_dpll_source_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(dpll, pin, pin_priv, state, extack,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_get - get dpll's source 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 source priority on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_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_pf *pf = pin_priv;
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		NL_SET_ERR_MSG(extack, "dpll not found");
>>+		goto unlock;
>>+	}
>>+	*prio = d->input_prio[p->idx];
>>+	ret = 0;
>>+unlock:
>>+	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_source_prio_set - set dpll source 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: source priority to be set on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_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_pf *pf = pin_priv;
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+
>>+	if (prio > ICE_DPLL_PRIO_MAX) {
>>+		NL_SET_ERR_MSG(extack, "prio out of range");
>>+		return ret;
>>+	}
>>+
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p) {
>>+		NL_SET_ERR_MSG(extack, "pin not found");
>>+		goto unlock;
>>+	}
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		NL_SET_ERR_MSG(extack, "dpll not found");
>>+		goto unlock;
>>+	}
>>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>>+	if (ret)
>>+		NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %d", ret);
>
>Why you need to print "ret"? It is propagated to the caller as a return
>value.
>

Fixed.

>
>>+unlock:
>>+	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_source_direction - callback for get source 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 source pin direction
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting direction of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ */
>>+static int ice_dpll_source_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_SOURCE;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_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
>>+ *
>>+ * 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,
>>+					  const enum dpll_pin_state state,
>>+					  struct netlink_ext_ack *extack) {
>>+	bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>+	struct ice_pf *pf = pin_priv;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (!pf)
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>
>Again, this does not make sense. You need struct ice_dpll_pin * related to
>parent. That should be parent priv and passed to dpll subsystem during
>registration and put as an "void * parent_pin_priv" arg to
>.state_on_pin_set() op. Whenever you do lookup like this, it is most
>usually wrong.
>

Fixed.

>
>
>>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>
>Can't you just store idx in struct ice_dpll_pin to avoid lookups like this
>one?
>

Fixed.

>
>>+			hw_idx = i;
>>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+
>>+	if ((enable && !!(p->flags[hw_idx] &
>>+			 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN)) ||
>>+	    (!enable && !(p->flags[hw_idx] &
>>+			  ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))) {
>>+		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 ret:%d\n",
>>+		__func__, parent_pin, pin, pf, 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.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>
>I wonder how valuable this return values table is for a reader.
>Not much I suppose. Do you need it in comments to all the functions you
>have here?
>

0 is success - most impoertant. I will leave those as is.

>
>>+ */
>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>+					  void *pin_priv,
>>+					  const struct dpll_pin *parent_pin,
>>+					  enum dpll_pin_state *state,
>>+					  struct netlink_ext_ack *extack) {
>>+	struct ice_pf *pf = pin_priv;
>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>
>Reverse christmas tree ordering please.

Fixed.

>
>
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	if (!pf)
>
>How exacly this can happen. My wild guess is it can't. Don't do such
>pointless checks please, confuses the reader.
>

From driver perspective the pf pointer value is given by external entity,
why shouldn't it be valdiated?

>
>>+		return ret;
>>+	if (ice_dpll_cb_lock(pf))
>>+		return -EBUSY;
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	parent_idx = ice_find_pin_idx(pf, parent_pin,
>>+				      ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (parent_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		if (pf->dplls.rclk.parent_idx[i] == parent_idx)
>>+			hw_idx = i;
>>+	if (hw_idx == ICE_DPLL_PIN_IDX_INVALID)
>>+		goto unlock;
>>+
>>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (ret)
>>+		goto unlock;
>>+
>>+	if (!!(p->flags[hw_idx] &
>>+	    ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN))
>
>Avoid needless "!!".
>

Fixed.

>
>>+		*state = DPLL_PIN_STATE_CONNECTED;
>>+	else
>>+		*state = DPLL_PIN_STATE_DISCONNECTED;
>
>Use ternary operator perhaps?
>

Fixed.

>
>>+	ret = 0;
>>+unlock:
>>+	ice_dpll_cb_unlock(pf);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: parent:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, parent_pin, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>
>const.
>

Fixed.

>
>>+	.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_source_direction, };
>>+
>>+static struct dpll_pin_ops ice_dpll_source_ops = {
>
>const.
>

Fixed.

>
>>+	.frequency_get = ice_dpll_source_frequency_get,
>>+	.frequency_set = ice_dpll_source_frequency_set,
>>+	.state_on_dpll_get = ice_dpll_source_state_get,
>>+	.state_on_dpll_set = ice_dpll_source_state_set,
>>+	.prio_get = ice_dpll_source_prio_get,
>>+	.prio_set = ice_dpll_source_prio_set,
>>+	.direction_get = ice_dpll_source_direction, };
>>+
>>+static struct dpll_pin_ops ice_dpll_output_ops = {
>
>const.
>

Fixed.

>>+	.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 struct dpll_device_ops ice_dpll_ops = {
>
>const.
>

Fixed.

>
>>+	.lock_status_get = ice_dpll_lock_status_get,
>>+	.mode_get = ice_dpll_mode_get,
>>+	.mode_supported = ice_dpll_mode_supported, };
>>+
>>+/**
>>+ * ice_dpll_release_info - release memory allocated for pins
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ */
>>+static void ice_dpll_release_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_release_rclk_pin - release rclk pin from its parents
>>+ * @pf: board private structure
>>+ *
>>+ * Deregister from parent pins and release resources in dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_release_rclk_pin(struct ice_pf *pf) {
>>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>>+	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;
>>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>>+					   &ice_dpll_rclk_ops, pf);
>>+	}
>>+	dpll_pin_put(rclk->pin);
>>+	rclk->pin = NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release_pins - release pin's from dplls registered in
>>+subsystem
>>+ * @pf: board private structure
>>+ * @dpll_eec: dpll_eec dpll pointer
>>+ * @dpll_pps: dpll_pps dpll pointer
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Deregister and free pins of a given array of pins from dpll devices
>>+ * registered in dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_release_pins(struct ice_pf *pf, struct dpll_device *dpll_eec,
>>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>>+		      int count, struct dpll_pin_ops *ops, bool cgu) {
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		struct ice_dpll_pin *p = &pins[i];
>>+
>>+		if (p && !IS_ERR_OR_NULL(p->pin)) {
>>+			if (cgu && dpll_eec)
>>+				dpll_pin_unregister(dpll_eec, p->pin, ops, pf);
>>+			if (cgu && dpll_pps)
>>+				dpll_pin_unregister(dpll_pps, p->pin, ops, pf);
>>+			dpll_pin_put(p->pin);
>>+			p->pin = NULL;
>>+		}
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Register source or output pins within given DPLL in a Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int ice_dpll_register_pins(struct ice_pf *pf, bool cgu) {
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	u32 rclk_idx;
>>+	int ret, i;
>>+
>>+	ops = &ice_dpll_source_ops;
>>+	pins = pf->dplls.inputs;
>>+	for (i = 0; i < pf->dplls.num_inputs; i++) {
>>+		pins[i].pin = dpll_pin_get(pf->dplls.clock_id, i,
>>+					   THIS_MODULE, &pins[i].prop);
>>+		if (IS_ERR_OR_NULL(pins[i].pin)) {
>
>How exactly dpll_pin_get() can return NULL? It can't, use IS_ERR.
>Same in ice_dpll_release_pins() and two occurances below.
>

Fixed.

>
>
>>+			pins[i].pin = NULL;
>>+			return -ENOMEM;
>>+		}
>>+		if (cgu) {
>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>+						pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>+						pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>
>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>here.
>

No, in case of error, the caller releases everything ice_dpll_release_all(..).

>
>>+		}
>>+	}
>>+	if (cgu) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>+						   i + pf->dplls.num_inputs,
>>+						   THIS_MODULE, &pins[i].prop);
>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>+				pins[i].pin = NULL;
>>+				return -ENOMEM;
>
>Don't make up error values when you get them from the function you call:
>	return PTR_ERR(pins[i].pin);

Fixed.

>
>>+			}
>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>+						ops, pf, NULL);
>>+			if (ret)
>>+				return ret;
>
>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>here.
>

As above, in case of error, the caller releases everything.

>
>>+                                              ops, pf, NULL);
>
>
>>+		}
>>+	}
>>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf-
>>hw.pf_id;
>>+	pf->dplls.rclk.pin = dpll_pin_get(pf->dplls.clock_id, rclk_idx,
>>+					  THIS_MODULE, &pf->dplls.rclk.prop);
>>+	if (IS_ERR_OR_NULL(pf->dplls.rclk.pin)) {
>>+		pf->dplls.rclk.pin = NULL;
>>+		return -ENOMEM;
>
>Don't make up error values when you get them from the function you call:
>	return PTR_ERR(pf->dplls.rclk.pin);
>

Fixed.

>
>>+	}
>>+	ops = &ice_dpll_rclk_ops;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>>+		struct dpll_pin *parent =
>>+			pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>>+
>>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>>+					       ops, pf, dev);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>>+ * @pf: board private structure
>>+ * @clock_id: holds generated clock_id
>>+ *
>>+ * Generates unique (per board) clock_id for allocation and search of
>>+dpll
>>+ * devices in Linux dpll subsystem.
>>+ */
>>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id) {
>>+	*clock_id = pci_get_dsn(pf->pdev);
>>+}
>
>How about:
>
>static u64 ice_generate_clock_id(struct ice_pf *pf) {
>	return pci_get_dsn(pf->pdev);
>}
>
>??
>

Fixed.

>
>>+
>>+/**
>>+ * ice_dpll_init_dplls
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Get dplls instances for this board, if cgu is controlled by this
>>+NIC,
>>+ * register dpll with callbacks ops
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation fails
>>+ */
>>+static int ice_dpll_init_dplls(struct ice_pf *pf, bool cgu) {
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	int ret = -ENOMEM;
>>+	u64 clock_id;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+	pf->dplls.eec.dpll = dpll_device_get(clock_id, pf-
>>dplls.eec.dpll_idx,
>>+					     THIS_MODULE);
>>+	if (!pf->dplls.eec.dpll) {
>
>You have to use IS_ERR()
>

Fixed.

>
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (eec)\n");
>>+		return ret;
>>+	}
>>+	pf->dplls.pps.dpll = dpll_device_get(clock_id, pf-
>>dplls.pps.dpll_idx,
>>+					     THIS_MODULE);
>>+	if (!pf->dplls.pps.dpll) {
>
>You have to use IS_ERR()
>

Fixed.

>
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_get failed (pps)\n");
>>+		goto put_eec;
>>+	}
>>+
>>+	if (cgu) {
>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>+					   &ice_dpll_ops, pf, dev);
>>+		if (ret)
>>+			goto put_pps;
>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>+					   &ice_dpll_ops, pf, dev);
>>+		if (ret)
>
>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>DPLL_TYPE_EEC here. Fix the error path.
>

The caller shall do the clean up, but yeah will fix this as here clean up
is not expected.

>
>>+			goto put_pps;
>>+	}
>>+
>>+	return 0;
>>+
>>+put_pps:
>>+	dpll_device_put(pf->dplls.pps.dpll);
>>+	pf->dplls.pps.dpll = NULL;
>>+put_eec:
>>+	dpll_device_put(pf->dplls.eec.dpll);
>>+	pf->dplls.eec.dpll = NULL;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * 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.
>>+ * 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->source_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_source_idx, d->source_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_source = pf->dplls.inputs[d->source_idx].pin;
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		return ice_dpll_pin_state_update(pf, p,
>>+						 ICE_DPLL_PIN_TYPE_SOURCE);
>>+	}
>>+	if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>>+	    d->dpll_state == ICE_CGU_STATE_FREERUN) {
>>+		d->active_source = NULL;
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		d->prev_source_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+		d->source_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+		ret = ice_dpll_pin_state_update(pf, p,
>>ICE_DPLL_PIN_TYPE_SOURCE);
>>+	} else if (d->source_idx != d->prev_source_idx) {
>>+		p = &pf->dplls.inputs[d->prev_source_idx];
>>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>>+		p = &pf->dplls.inputs[d->source_idx];
>>+		d->active_source = p->pin;
>>+		ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOURCE);
>>+		d->prev_source_idx = d->source_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_notify(d->dpll, DPLL_A_LOCK_STATUS);
>>+	}
>>+	if (d->prev_source != d->active_source) {
>>+		d->prev_source = d->active_source;
>>+		if (d->active_source)
>>+			dpll_pin_notify(d->dpll, d->active_source,
>>+					DPLL_A_PIN_STATE);
>
>Didn't the state of the previously active source change as well? You need
>to send notification for that too.
>

Makes sense, fixed.

>
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>+ * @work: pointer to kthread_work structure
>>+ *
>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>+ */
>>+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.
>>+ *
>>+ * 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_release_all - disable support for DPLL and unregister dpll
>>+device
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is controlled by this driver instance
>>+ *
>>+ * This function handles the cleanup work required from the
>>+initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ *
>>+ * Context: Called under pf->dplls.lock  */ static void
>>+ice_dpll_release_all(struct ice_pf *pf, bool cgu) {
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	ice_dpll_release_rclk_pin(pf);
>>+	ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
>>+			      d->num_inputs, &ice_dpll_source_ops, cgu);
>>+	mutex_unlock(&pf->dplls.lock);
>>+	if (cgu) {
>>+		mutex_lock(&pf->dplls.lock);
>
>Interesting, you lock again a lock you just unlocked. One might wonder why
>you just don't move the call to mutex_unlock below this "if section".
>

Fixed.

>
>>+		ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
>>+				      d->num_outputs,
>>+				      &ice_dpll_output_ops, cgu);
>>+		mutex_unlock(&pf->dplls.lock);
>>+	}
>>+	ice_dpll_release_info(pf);
>>+	if (dp->dpll) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		if (cgu)
>>+			dpll_device_unregister(dp->dpll, &ice_dpll_ops, pf);
>>+		dpll_device_put(dp->dpll);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>>+	}
>>+
>>+	if (de->dpll) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		if (cgu)
>>+			dpll_device_unregister(de->dpll, &ice_dpll_ops, pf);
>>+		dpll_device_put(de->dpll);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>>+	}
>>+
>>+	if (cgu) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		kthread_cancel_delayed_work_sync(&d->work);
>>+		if (d->kworker) {
>>+			kthread_destroy_worker(d->kworker);
>>+			d->kworker = NULL;
>>+			dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>>+		}
>>+		mutex_unlock(&pf->dplls.lock);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and
>>+unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * Handles the cleanup work required after dpll initialization,
>>+ * freeing resources and unregistering the dpll.
>>+ */
>>+void ice_dpll_release(struct ice_pf *pf) {
>>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		ice_dpll_release_all(pf,
>>+				     ice_is_feature_supported(pf, ICE_F_CGU));
>>+		mutex_destroy(&pf->dplls.lock);
>>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_direct_pins - initializes source or output pins
>>+information
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information about input or output pins, cache them in pins struct.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int
>>+ice_dpll_init_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;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+		input = true;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+		input = false;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].prop.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;
>
>It is a flag. Common is to use bit op &= instead.
>

Fixed.

>
>>+		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;
>>+	}
>>+
>>+	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
>>+ */
>>+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.label = dev_name(dev);
>>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	pin->prop.capabilities += DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+
>>+	return ice_dpll_pin_state_update(pf, pin,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - init pins wrapper
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Wraps functions for pin inti.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf,
>>+			      const enum ice_dpll_pin_type pin_type) {
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE)
>>+		return ice_dpll_init_direct_pins(pf, pin_type);
>>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>>+		return ice_dpll_init_direct_pins(pf, pin_type);
>>+	else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE)
>>+		return ice_dpll_init_rclk_pin(pf);
>>+	else
>>+		return -EINVAL;
>
>How this can happen?
>
>How about:
>	switch (pin_type) {
>	case ICE_DPLL_PIN_TYPE_SOURCE:
>	case ICE_DPLL_PIN_TYPE_OUTPUT:
>		return ice_dpll_init_direct_pins(pf, pin_type);
>	case ICE_DPLL_PIN_TYPE_RCLK_SOURCE:
>		return ice_dpll_init_rclk_pin(pf);
>	}
>?
>

It shall not if called properly, changed to switch case as suggested.
Altough still needs default: as there is one not covered case, thus compiler
warns about it.

>
>
>>+}
>>+
>>+/**
>>+ * 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 - error
>>+ */
>>+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;
>>+	u8 base_rclk_idx;
>>+
>>+	ice_generate_clock_id(pf, &d->clock_id);
>>+	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(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	if (cgu) {
>>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+		if (!d->outputs)
>>+			goto release_info;
>>+
>>+		ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+		if (ret)
>>+			goto release_info;
>>+	}
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &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] = base_rclk_idx + i;
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	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;
>>+
>>+release_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_release_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - initialize dplls support
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device dplls registering 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
>>+ */
>>+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 release;
>>+	err = ice_dpll_init_dplls(pf, cgu_present);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, cgu_present);
>
>This should be rather called "ice_dpll_init_pins()" to be in sync with
>ice_dpll_init_dplls() as it is doing more then just registration.
>

Makes sense, fixed.

Thank you very much!
Arkadiusz

>
>>+	if (err)
>>+		goto release;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	if (cgu_present) {
>>+		err = ice_dpll_init_worker(pf);
>>+		if (err)
>>+			goto release;
>>+	}
>>+	mutex_unlock(&d->lock);
>>+	dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>>+
>>+	return err;
>>+
>>+release:
>>+	ice_dpll_release_all(pf, cgu_present);
>>+	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\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..aad48b9910b7
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,101 @@
>>+/* 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
>>+ * @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;
>>+	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
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @source_idx: source currently selected
>>+ * @prev_source_idx: source previously selected
>>+ * @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_source: pointer to active source pin
>>+ * @prev_source: pointer to previous active source pin  */ struct
>>+ice_dpll {
>>+	struct dpll_device *dpll;
>>+	int dpll_idx;
>>+	u8 source_idx;
>>+	u8 prev_source_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_source;
>>+	struct dpll_pin *prev_source;
>>+};
>>+
>>+/** 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
>>+ * @num_rclk: number of recovered clock pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic
>>+work  */ 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_release(struct ice_pf *pf);
>>+
>>+int ice_dpll_rclk_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_rclk_release(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 a1f7c8edc22f..6b28b95a7254 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4821,6 +4821,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"); @@ -
>4847,6
>>+4851,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_release(pf);
>> }
>>
>> static void ice_init_wakeup(struct ice_pf *pf) diff --git
>>a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>index e9a371fa038b..39b692945f73 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>>@@ -3609,28 +3609,31 @@ enum dpll_pin_type ice_cgu_get_pin_type(struct
>>ice_hw *hw, u8 pin, bool input)  }
>>
>> /**
>>- * ice_cgu_get_pin_sig_type_mask
>>+ * 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
>>  *
>>- * Return: signal type bit mask of a pin.
>>+ * Get frequency supported number and array of supported frequencies.
>>+ *
>>+ * Return: array of supported frequencies for given pin.
>>  */
>>-unsigned long
>>-ice_cgu_get_pin_freq_mask(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 struct ice_cgu_pin_desc *t;
>> 	int t_size;
>>
>>+	*num = 0;
>> 	t = ice_cgu_get_pin_desc(hw, input, &t_size);
>>-
>> 	if (!t)
>>-		return 0;
>>-
>>+		return NULL;
>> 	if (pin >= t_size)
>>-		return 0;
>>+		return NULL;
>>+	*num = t[pin].freq_supp_num;
>>
>>-	return t[pin].sig_type_mask;
>>+	return t[pin].freq_supp;
>> }
>>
>> /**
>>diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>index d09e5bca0ff1..4568b0403cd7 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>>@@ -192,147 +192,137 @@ enum ice_si_cgu_out_pins {
>> 	NUM_SI_CGU_OUTPUT_PINS
>> };
>>
>>-#define MAX_CGU_PIN_NAME_LEN		16
>>-#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_FREQ_SUPP_1_HZ) | \
>>-					 BIT(DPLL_PIN_FREQ_SUPP_10_MHZ))
>>+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[MAX_CGU_PIN_NAME_LEN];
>>+	char *name;
>> 	u8 index;
>> 	enum dpll_pin_type type;
>>-	unsigned long sig_type_mask;
>>+	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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
>>-	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+	{ "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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>-	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> 	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
>> 	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>-	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>>-			BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		BIT(DPLL_PIN_FREQ_SUPP_1_HZ) },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> 	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_10_MHZ) },
>>-	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>-	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>-		BIT(DPLL_PIN_FREQ_SUPP_UNSPEC) },
>>+		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,
>>-		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
>>+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> 	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
>> };
>>
>>@@ -429,8 +419,8 @@ 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); -unsigned
>>long -ice_cgu_get_pin_freq_mask(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,
>>--
>>2.34.1
>>


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-15 22:07       ` Kubalewski, Arkadiusz
@ 2023-05-16  6:26         ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16  6:26 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, May 3, 2023 2:19 PM
>>
>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>+ * 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
>>>+ * @frequency: frequency to be set
>>>+ * @extack: error reporting
>>>+ * @pin_type: type of pin being configured
>>>+ *
>>>+ * Wraps internal set frequency command on a pin.
>>>+ *
>>>+ * 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,
>>>+		       const u32 frequency,
>>>+		       struct netlink_ext_ack *extack,
>>>+		       const enum ice_dpll_pin_type pin_type) {
>>>+	struct ice_pf *pf = pin_priv;
>>>+	struct ice_dpll_pin *p;
>>>+	int ret = -EINVAL;
>>>+
>>>+	if (!pf)
>>>+		return ret;
>>>+	if (ice_dpll_cb_lock(pf))
>>>+		return -EBUSY;
>>>+	p = ice_find_pin(pf, pin, pin_type);
>>
>>This does not make any sense to me. You should avoid the lookups and remove
>>ice_find_pin() function entirely. The purpose of having pin_priv is to
>>carry the struct ice_dpll_pin * directly. You should pass it down during
>>pin register.
>>
>>pf pointer is stored in dpll_priv.
>>
>
>In this case dpll_priv is not passed, so cannot use it.

It should be passed. In general to every op where *dpll is passed, the
dpll_priv pointer should be passed along. Please, fix this.


>But in general it makes sense I will hold pf inside of ice_dpll_pin
>and fix this.

Nope, just use dpll_priv. That's why we have it.


[...]


>>>+/**
>>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>>+ * @dpll: dpll being configured
>>>+ * @pin: pointer to a pin
>>>+ * @pin_priv: private data pointer passed on pin registration
>>>+ * @state: state of pin to be set
>>>+ * @extack: error reporting
>>>+ * @pin_type: type of a pin
>>>+ *
>>>+ * Set pin state on a pin.
>>>+ *
>>>+ * Return:
>>>+ * * 0 - OK or no change required
>>>+ * * negative - error
>>>+ */
>>>+static int
>>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>>+		       const struct dpll_pin *pin, void *pin_priv,
>>>+		       const enum dpll_pin_state state,
>>
>>Why you use const with enums?
>>
>
>Just show usage intention explicitly.

Does not make any sense what so ever. Please avoid it.


>>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>>+					  void *pin_priv,
>>>+					  const struct dpll_pin *parent_pin,
>>>+					  enum dpll_pin_state *state,
>>>+					  struct netlink_ext_ack *extack) {
>>>+	struct ice_pf *pf = pin_priv;
>>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>
>>Reverse christmas tree ordering please.
>
>Fixed.
>
>>
>>
>>>+	struct ice_dpll_pin *p;
>>>+	int ret = -EFAULT;
>>>+
>>>+	if (!pf)
>>
>>How exacly this can happen. My wild guess is it can't. Don't do such
>>pointless checks please, confuses the reader.
>>
>
>From driver perspective the pf pointer value is given by external entity,
>why shouldn't it be valdiated?

What? You pass it during register, you get it back here. Nothing to
check. Please drop it. Non-sense checks like this have no place in
kernel, they only confuse reader as he/she assumes it is a valid case.


[...]


>>
>>
>>>+			pins[i].pin = NULL;
>>>+			return -ENOMEM;
>>>+		}
>>>+		if (cgu) {
>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>+						pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>+						pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>
>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>here.
>>
>
>No, in case of error, the caller releases everything ice_dpll_release_all(..).


How does ice_dpll_release_all() where you failed? If you need to
unregister one or both or none? I know that in ice you have odd ways to
handle error paths in general, but this one clearly seems to be broken.





>
>>
>>>+		}
>>>+	}
>>>+	if (cgu) {
>>>+		ops = &ice_dpll_output_ops;
>>>+		pins = pf->dplls.outputs;
>>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>>+						   i + pf->dplls.num_inputs,
>>>+						   THIS_MODULE, &pins[i].prop);
>>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>>+				pins[i].pin = NULL;
>>>+				return -ENOMEM;
>>
>>Don't make up error values when you get them from the function you call:
>>	return PTR_ERR(pins[i].pin);
>
>Fixed.
>
>>
>>>+			}
>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>
>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>here.
>>
>
>As above, in case of error, the caller releases everything.

As above, I don't think it works.


[...]


>>>+	}
>>>+
>>>+	if (cgu) {
>>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>>+					   &ice_dpll_ops, pf, dev);
>>>+		if (ret)
>>>+			goto put_pps;
>>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>>+					   &ice_dpll_ops, pf, dev);
>>>+		if (ret)
>>
>>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>>DPLL_TYPE_EEC here. Fix the error path.
>>
>
>The caller shall do the clean up, but yeah will fix this as here clean up
>is not expected.

:) Just make your error paths obvious and easy to follow to not to
confuse anybody, you included.


>
>>
>>>+			goto put_pps;
>>>+	}
>>>+
>>>+	return 0;
>>>+
>>>+put_pps:
>>>+	dpll_device_put(pf->dplls.pps.dpll);
>>>+	pf->dplls.pps.dpll = NULL;
>>>+put_eec:
>>>+	dpll_device_put(pf->dplls.eec.dpll);
>>>+	pf->dplls.eec.dpll = NULL;
>>>+
>>>+	return ret;
>>>+}

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-16  6:26         ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16  6:26 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, May 3, 2023 2:19 PM
>>
>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>+ * 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
>>>+ * @frequency: frequency to be set
>>>+ * @extack: error reporting
>>>+ * @pin_type: type of pin being configured
>>>+ *
>>>+ * Wraps internal set frequency command on a pin.
>>>+ *
>>>+ * 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,
>>>+		       const u32 frequency,
>>>+		       struct netlink_ext_ack *extack,
>>>+		       const enum ice_dpll_pin_type pin_type) {
>>>+	struct ice_pf *pf = pin_priv;
>>>+	struct ice_dpll_pin *p;
>>>+	int ret = -EINVAL;
>>>+
>>>+	if (!pf)
>>>+		return ret;
>>>+	if (ice_dpll_cb_lock(pf))
>>>+		return -EBUSY;
>>>+	p = ice_find_pin(pf, pin, pin_type);
>>
>>This does not make any sense to me. You should avoid the lookups and remove
>>ice_find_pin() function entirely. The purpose of having pin_priv is to
>>carry the struct ice_dpll_pin * directly. You should pass it down during
>>pin register.
>>
>>pf pointer is stored in dpll_priv.
>>
>
>In this case dpll_priv is not passed, so cannot use it.

It should be passed. In general to every op where *dpll is passed, the
dpll_priv pointer should be passed along. Please, fix this.


>But in general it makes sense I will hold pf inside of ice_dpll_pin
>and fix this.

Nope, just use dpll_priv. That's why we have it.


[...]


>>>+/**
>>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>>+ * @dpll: dpll being configured
>>>+ * @pin: pointer to a pin
>>>+ * @pin_priv: private data pointer passed on pin registration
>>>+ * @state: state of pin to be set
>>>+ * @extack: error reporting
>>>+ * @pin_type: type of a pin
>>>+ *
>>>+ * Set pin state on a pin.
>>>+ *
>>>+ * Return:
>>>+ * * 0 - OK or no change required
>>>+ * * negative - error
>>>+ */
>>>+static int
>>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>>+		       const struct dpll_pin *pin, void *pin_priv,
>>>+		       const enum dpll_pin_state state,
>>
>>Why you use const with enums?
>>
>
>Just show usage intention explicitly.

Does not make any sense what so ever. Please avoid it.


>>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>>+					  void *pin_priv,
>>>+					  const struct dpll_pin *parent_pin,
>>>+					  enum dpll_pin_state *state,
>>>+					  struct netlink_ext_ack *extack) {
>>>+	struct ice_pf *pf = pin_priv;
>>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>
>>Reverse christmas tree ordering please.
>
>Fixed.
>
>>
>>
>>>+	struct ice_dpll_pin *p;
>>>+	int ret = -EFAULT;
>>>+
>>>+	if (!pf)
>>
>>How exacly this can happen. My wild guess is it can't. Don't do such
>>pointless checks please, confuses the reader.
>>
>
>From driver perspective the pf pointer value is given by external entity,
>why shouldn't it be valdiated?

What? You pass it during register, you get it back here. Nothing to
check. Please drop it. Non-sense checks like this have no place in
kernel, they only confuse reader as he/she assumes it is a valid case.


[...]


>>
>>
>>>+			pins[i].pin = NULL;
>>>+			return -ENOMEM;
>>>+		}
>>>+		if (cgu) {
>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>+						pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>+						pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>
>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>here.
>>
>
>No, in case of error, the caller releases everything ice_dpll_release_all(..).


How does ice_dpll_release_all() where you failed? If you need to
unregister one or both or none? I know that in ice you have odd ways to
handle error paths in general, but this one clearly seems to be broken.





>
>>
>>>+		}
>>>+	}
>>>+	if (cgu) {
>>>+		ops = &ice_dpll_output_ops;
>>>+		pins = pf->dplls.outputs;
>>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>>+						   i + pf->dplls.num_inputs,
>>>+						   THIS_MODULE, &pins[i].prop);
>>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>>+				pins[i].pin = NULL;
>>>+				return -ENOMEM;
>>
>>Don't make up error values when you get them from the function you call:
>>	return PTR_ERR(pins[i].pin);
>
>Fixed.
>
>>
>>>+			}
>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>>+						ops, pf, NULL);
>>>+			if (ret)
>>>+				return ret;
>>
>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>here.
>>
>
>As above, in case of error, the caller releases everything.

As above, I don't think it works.


[...]


>>>+	}
>>>+
>>>+	if (cgu) {
>>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>>+					   &ice_dpll_ops, pf, dev);
>>>+		if (ret)
>>>+			goto put_pps;
>>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>>+					   &ice_dpll_ops, pf, dev);
>>>+		if (ret)
>>
>>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>>DPLL_TYPE_EEC here. Fix the error path.
>>
>
>The caller shall do the clean up, but yeah will fix this as here clean up
>is not expected.

:) Just make your error paths obvious and easy to follow to not to
confuse anybody, you included.


>
>>
>>>+			goto put_pps;
>>>+	}
>>>+
>>>+	return 0;
>>>+
>>>+put_pps:
>>>+	dpll_device_put(pf->dplls.pps.dpll);
>>>+	pf->dplls.pps.dpll = NULL;
>>>+put_eec:
>>>+	dpll_device_put(pf->dplls.eec.dpll);
>>>+	pf->dplls.eec.dpll = NULL;
>>>+
>>>+	return ret;
>>>+}

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-15 17:12     ` Jiri Pirko
@ 2023-05-16  9:22       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16  9:22 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, May 15, 2023 7:13 PM
>
>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>
>[...]
>
>>+static const enum dpll_lock_status
>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>
>This is a bit confusing to me. You are locked, yet you report
>calibrating? Wouldn't it be better to have:
>DPLL_LOCK_STATUS_LOCKED
>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>
>?
>

Sure makes sense, will add this state.

>
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>+};
>
>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-16  9:22       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16  9:22 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, May 15, 2023 7:13 PM
>
>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>
>[...]
>
>>+static const enum dpll_lock_status
>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>
>This is a bit confusing to me. You are locked, yet you report
>calibrating? Wouldn't it be better to have:
>DPLL_LOCK_STATUS_LOCKED
>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>
>?
>

Sure makes sense, will add this state.

>
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>+};
>
>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-16  9:22       ` Kubalewski, Arkadiusz
@ 2023-05-16 11:46         ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16 11:46 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, May 15, 2023 7:13 PM
>>
>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>
>>[...]
>>
>>>+static const enum dpll_lock_status
>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>
>>This is a bit confusing to me. You are locked, yet you report
>>calibrating? Wouldn't it be better to have:
>>DPLL_LOCK_STATUS_LOCKED
>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>
>>?
>>
>
>Sure makes sense, will add this state.

Do you need "calibrating" then? I mean, the docs says:
  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
                                source pin signal

Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
Seems like you should have:
[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,

and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?

Also, as a sidenote, could you use the whole names of enum value names
in documentation? Simple reason, greppability.

Thanks!


>
>>
>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>+};
>>
>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-16 11:46         ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16 11:46 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, May 15, 2023 7:13 PM
>>
>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>
>>[...]
>>
>>>+static const enum dpll_lock_status
>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>
>>This is a bit confusing to me. You are locked, yet you report
>>calibrating? Wouldn't it be better to have:
>>DPLL_LOCK_STATUS_LOCKED
>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>
>>?
>>
>
>Sure makes sense, will add this state.

Do you need "calibrating" then? I mean, the docs says:
  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
                                source pin signal

Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
Seems like you should have:
[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,

and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?

Also, as a sidenote, could you use the whole names of enum value names
in documentation? Simple reason, greppability.

Thanks!


>
>>
>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>+};
>>
>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-15  9:30               ` Jiri Pirko
@ 2023-05-16 12:05                 ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16 12:05 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, May 15, 2023 11:31 AM
>
>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>
>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>
>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>> >+definitions:
>>>>>> >+  -
>>>>>> >+    type: enum
>>>>>> >+    name: mode
>>>>>> >+    doc: |
>>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>>selects
>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>DPLL_A_MODE
>>>>>> >+      attribute
>>>>>> >+    entries:
>>>>>> >+      -
>>>>>> >+        name: unspec
>>>>>>
>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>> What is the usecase. If there isn't please remove.
>>>>>
>>>>>+1
>>>>>
>>>>
>>>>Sure, fixed.
>>>>
>>>>>> >+        doc: unspecified value
>>>>>> >+      -
>>>>>> >+        name: manual
>>>>>
>>>>>I think the documentation calls this "forced", still.
>>>>>
>>>>
>>>>Yes, good catch, fixed docs.
>>>>
>>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>>> >+      -
>>>>>> >+        name: automatic
>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>> >+      -
>>>>>> >+        name: holdover
>>>>>> >+        doc: dpll forced into holdover mode
>>>>>> >+      -
>>>>>> >+        name: freerun
>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>
>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>
>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>at play here?
>>>>
>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>seconds before it locks to a signal once it is selected.
>>>>
>>>>>
>>>>>> >+      -
>>>>>> >+        name: nco
>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>
>>>>>Noob question, what is NCO in terms of implementation?
>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>the control? Or we always use system refclk and then tune?
>>>>>
>>>>
>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>it
>>>
>>>So how exactly this is different to freerun? Does user care or he would
>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>device specific thing that should be abstracted out here?
>>>
>>
>>Sure, it is device specific, some synchronizing circuits would have this
>>capability, while others would not.
>>Should be abstracted out? It is a good question.. shall user know that he is
>>in
>>freerun with possibility to control the frequency or not?
>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>supporting multiple output frequencies.
>>How the one would know if those frequencies are supported only in
>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>In other words: As the user can I change a frequency of a dpll if active
>>mode is FREERUN?
>
>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>understanding that you can control frequency in NCO mode is not present
>:/ That only means it may be confusing and not described properly.
>How do you control this frequency exactly? I see no such knob.
>

The set frequency is there already, although we miss phase offset I guess.

But I have changed my mind on having this in the kernel..
Initially I have added this mode as our HW supports it, while thinking that
dpll subsystem shall have this, and we will implement it one day..
But as we have not implemented it yet, let's leave work and discussion on
this mode for the future, when someone will actually try to implement it.

>Can't the oscilator be modeled as a pin and then you are not in freerun
>but locked this "internal pin"? We know how to control frequency there.
>

Hmm, yeah probably could work this way.


Thank you!
Arkadiusz

>
>>
>>I would say it is better to have such mode, we could argue on naming though.
>>
>>>
>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>dividers before it reaches the output).
>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>SYSTEM CLOCK.
>>>>In this case control over output frequency is done by synchronizer chip
>>>>firmware, but still it will not lock to any source pin signal.
>>>>
>>>>>> >+    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: unspec
>>>>>> >+        doc: unspecified value
>>>>>> >+      -
>>>>>> >+        name: unlocked
>>>>>> >+        doc: |
>>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>> >+      -
>>>>>> >+        name: calibrating
>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>> >+      -
>>>>>> >+        name: locked
>>>>>> >+        doc: dpll is locked
>>>>>> >+      -
>>>>>> >+        name: holdover
>>>>>> >+        doc: |
>>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>
>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>
>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>state then we need some extra atomicity requirements on the SET
>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>lock status when request reached the HW.
>>>>>
>>>>
>>>>Improved the docs:
>>>>        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 it was not, the dpll's lock-status will remain
>>>
>>>"if it was not" does not really cope with the sentence above that. Could
>>>you iron-out the phrasing a bit please?
>>
>>
>>Hmmm,
>>        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)
>>
>>Hope this is better?
>
>Okay.
>
>>
>>
>>Thank you!
>>Arkadiusz
>>
>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-16 12:05                 ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16 12:05 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, May 15, 2023 11:31 AM
>
>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>
>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>
>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>> >+definitions:
>>>>>> >+  -
>>>>>> >+    type: enum
>>>>>> >+    name: mode
>>>>>> >+    doc: |
>>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>>selects
>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>DPLL_A_MODE
>>>>>> >+      attribute
>>>>>> >+    entries:
>>>>>> >+      -
>>>>>> >+        name: unspec
>>>>>>
>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>> What is the usecase. If there isn't please remove.
>>>>>
>>>>>+1
>>>>>
>>>>
>>>>Sure, fixed.
>>>>
>>>>>> >+        doc: unspecified value
>>>>>> >+      -
>>>>>> >+        name: manual
>>>>>
>>>>>I think the documentation calls this "forced", still.
>>>>>
>>>>
>>>>Yes, good catch, fixed docs.
>>>>
>>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>>> >+      -
>>>>>> >+        name: automatic
>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>> >+      -
>>>>>> >+        name: holdover
>>>>>> >+        doc: dpll forced into holdover mode
>>>>>> >+      -
>>>>>> >+        name: freerun
>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>
>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>
>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>at play here?
>>>>
>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>seconds before it locks to a signal once it is selected.
>>>>
>>>>>
>>>>>> >+      -
>>>>>> >+        name: nco
>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>
>>>>>Noob question, what is NCO in terms of implementation?
>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>the control? Or we always use system refclk and then tune?
>>>>>
>>>>
>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>it
>>>
>>>So how exactly this is different to freerun? Does user care or he would
>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>device specific thing that should be abstracted out here?
>>>
>>
>>Sure, it is device specific, some synchronizing circuits would have this
>>capability, while others would not.
>>Should be abstracted out? It is a good question.. shall user know that he is
>>in
>>freerun with possibility to control the frequency or not?
>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>supporting multiple output frequencies.
>>How the one would know if those frequencies are supported only in
>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>In other words: As the user can I change a frequency of a dpll if active
>>mode is FREERUN?
>
>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>understanding that you can control frequency in NCO mode is not present
>:/ That only means it may be confusing and not described properly.
>How do you control this frequency exactly? I see no such knob.
>

The set frequency is there already, although we miss phase offset I guess.

But I have changed my mind on having this in the kernel..
Initially I have added this mode as our HW supports it, while thinking that
dpll subsystem shall have this, and we will implement it one day..
But as we have not implemented it yet, let's leave work and discussion on
this mode for the future, when someone will actually try to implement it.

>Can't the oscilator be modeled as a pin and then you are not in freerun
>but locked this "internal pin"? We know how to control frequency there.
>

Hmm, yeah probably could work this way.


Thank you!
Arkadiusz

>
>>
>>I would say it is better to have such mode, we could argue on naming though.
>>
>>>
>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>dividers before it reaches the output).
>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>SYSTEM CLOCK.
>>>>In this case control over output frequency is done by synchronizer chip
>>>>firmware, but still it will not lock to any source pin signal.
>>>>
>>>>>> >+    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: unspec
>>>>>> >+        doc: unspecified value
>>>>>> >+      -
>>>>>> >+        name: unlocked
>>>>>> >+        doc: |
>>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>> >+      -
>>>>>> >+        name: calibrating
>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>> >+      -
>>>>>> >+        name: locked
>>>>>> >+        doc: dpll is locked
>>>>>> >+      -
>>>>>> >+        name: holdover
>>>>>> >+        doc: |
>>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>
>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>
>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>state then we need some extra atomicity requirements on the SET
>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>lock status when request reached the HW.
>>>>>
>>>>
>>>>Improved the docs:
>>>>        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 it was not, the dpll's lock-status will remain
>>>
>>>"if it was not" does not really cope with the sentence above that. Could
>>>you iron-out the phrasing a bit please?
>>
>>
>>Hmmm,
>>        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)
>>
>>Hope this is better?
>
>Okay.
>
>>
>>
>>Thank you!
>>Arkadiusz
>>
>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-11 23:29             ` Jakub Kicinski
@ 2023-05-16 12:15                 ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16 12:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko


>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Friday, May 12, 2023 1:29 AM
>
>On Thu, 11 May 2023 20:53:40 +0000 Kubalewski, Arkadiusz wrote:
>> >Because I think that'd be done by an NCO, no?
>>
>> From docs I can also see that chip has additional designated dpll for NCO
>>mode,
>> and this statement:
>> "Numerically controlled oscillator (NCO) behavior allows system software
>>to steer
>> DPLL frequency or synthesizer frequency with resolution better than 0.005
>>ppt."
>>
>> I am certainly not an expert on this, but seems like the NCO mode for
>this chip
>> is better than FREERUN, since signal produced on output is somehow higher
>quality.
>
>Herm, this seems complicated. Do you have a use case for this?
>Maybe we can skip it :D
>

True!
Actually yeah, I agree let's skip it, we don't have implemented example and
there is no need for it for now, we will have to discuss it someday if someone
want to give control over it to the user, and as Jiri already pointed, probably
it could be done with using internal oscillator type of pin and just
configurable frequency.

Thus I will remove the NCO from the dpll modes.

>My reading of the quote is that there is an NCO which SW can control
>precisely. But that does not answer the questions:
> - is the NCO driven by system clock 

Yes it is, other part of documentation lead to this conclusion.

> or can it be used in locked mode?

There is also a behavior called "NCO-hybrid", and as I understand works
somehow as you described, dpll can lock to something but additional offset
based on so called "System Clock Input (Jitter Reference)" pin is applied
to the signal (the pin is also used for normal NCO mode).

> - what is the "system software"? FW which based on temperature
>   information, and whatever else compensates drift of system clock?
>   or there are exposed registers to control the NCO?

The synchronizer chip firmware provides the knobs in form of registers to
control frequency offset for all the outputs, as well as for dpll's
frequency offset.

Thank you,
Arkadiusz


^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-16 12:15                 ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-16 12:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Vadim Fedorenko, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Vadim Fedorenko


>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Friday, May 12, 2023 1:29 AM
>
>On Thu, 11 May 2023 20:53:40 +0000 Kubalewski, Arkadiusz wrote:
>> >Because I think that'd be done by an NCO, no?
>>
>> From docs I can also see that chip has additional designated dpll for NCO
>>mode,
>> and this statement:
>> "Numerically controlled oscillator (NCO) behavior allows system software
>>to steer
>> DPLL frequency or synthesizer frequency with resolution better than 0.005
>>ppt."
>>
>> I am certainly not an expert on this, but seems like the NCO mode for
>this chip
>> is better than FREERUN, since signal produced on output is somehow higher
>quality.
>
>Herm, this seems complicated. Do you have a use case for this?
>Maybe we can skip it :D
>

True!
Actually yeah, I agree let's skip it, we don't have implemented example and
there is no need for it for now, we will have to discuss it someday if someone
want to give control over it to the user, and as Jiri already pointed, probably
it could be done with using internal oscillator type of pin and just
configurable frequency.

Thus I will remove the NCO from the dpll modes.

>My reading of the quote is that there is an NCO which SW can control
>precisely. But that does not answer the questions:
> - is the NCO driven by system clock 

Yes it is, other part of documentation lead to this conclusion.

> or can it be used in locked mode?

There is also a behavior called "NCO-hybrid", and as I understand works
somehow as you described, dpll can lock to something but additional offset
based on so called "System Clock Input (Jitter Reference)" pin is applied
to the signal (the pin is also used for normal NCO mode).

> - what is the "system software"? FW which based on temperature
>   information, and whatever else compensates drift of system clock?
>   or there are exposed registers to control the NCO?

The synchronizer chip firmware provides the knobs in form of registers to
control frequency offset for all the outputs, as well as for dpll's
frequency offset.

Thank you,
Arkadiusz


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-16 12:05                 ` Kubalewski, Arkadiusz
@ 2023-05-16 14:33                   ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16 14:33 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, May 15, 2023 11:31 AM
>>
>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>
>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>
>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>> >+definitions:
>>>>>>> >+  -
>>>>>>> >+    type: enum
>>>>>>> >+    name: mode
>>>>>>> >+    doc: |
>>>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>>>selects
>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>DPLL_A_MODE
>>>>>>> >+      attribute
>>>>>>> >+    entries:
>>>>>>> >+      -
>>>>>>> >+        name: unspec
>>>>>>>
>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>
>>>>>>+1
>>>>>>
>>>>>
>>>>>Sure, fixed.
>>>>>
>>>>>>> >+        doc: unspecified value
>>>>>>> >+      -
>>>>>>> >+        name: manual
>>>>>>
>>>>>>I think the documentation calls this "forced", still.
>>>>>>
>>>>>
>>>>>Yes, good catch, fixed docs.
>>>>>
>>>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>>>> >+      -
>>>>>>> >+        name: automatic
>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>> >+      -
>>>>>>> >+        name: holdover
>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>> >+      -
>>>>>>> >+        name: freerun
>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>
>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>
>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>at play here?
>>>>>
>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>seconds before it locks to a signal once it is selected.
>>>>>
>>>>>>
>>>>>>> >+      -
>>>>>>> >+        name: nco
>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>
>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>
>>>>>
>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>it
>>>>
>>>>So how exactly this is different to freerun? Does user care or he would
>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>device specific thing that should be abstracted out here?
>>>>
>>>
>>>Sure, it is device specific, some synchronizing circuits would have this
>>>capability, while others would not.
>>>Should be abstracted out? It is a good question.. shall user know that he is
>>>in
>>>freerun with possibility to control the frequency or not?
>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>supporting multiple output frequencies.
>>>How the one would know if those frequencies are supported only in
>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>In other words: As the user can I change a frequency of a dpll if active
>>>mode is FREERUN?
>>
>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>understanding that you can control frequency in NCO mode is not present
>>:/ That only means it may be confusing and not described properly.
>>How do you control this frequency exactly? I see no such knob.
>>
>
>The set frequency is there already, although we miss phase offset I guess.

Yeah, but on a pin, right?



>
>But I have changed my mind on having this in the kernel..
>Initially I have added this mode as our HW supports it, while thinking that
>dpll subsystem shall have this, and we will implement it one day..
>But as we have not implemented it yet, let's leave work and discussion on
>this mode for the future, when someone will actually try to implement it.

Yeah, let's drop it then. One less confusing thing to wrap a head around :)


>
>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>but locked this "internal pin"? We know how to control frequency there.
>>
>
>Hmm, yeah probably could work this way.
>
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>I would say it is better to have such mode, we could argue on naming though.
>>>
>>>>
>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>dividers before it reaches the output).
>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>SYSTEM CLOCK.
>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>
>>>>>>> >+    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: unspec
>>>>>>> >+        doc: unspecified value
>>>>>>> >+      -
>>>>>>> >+        name: unlocked
>>>>>>> >+        doc: |
>>>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>> >+      -
>>>>>>> >+        name: calibrating
>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>> >+      -
>>>>>>> >+        name: locked
>>>>>>> >+        doc: dpll is locked
>>>>>>> >+      -
>>>>>>> >+        name: holdover
>>>>>>> >+        doc: |
>>>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>
>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>
>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>lock status when request reached the HW.
>>>>>>
>>>>>
>>>>>Improved the docs:
>>>>>        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 it was not, the dpll's lock-status will remain
>>>>
>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>you iron-out the phrasing a bit please?
>>>
>>>
>>>Hmmm,
>>>        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)
>>>
>>>Hope this is better?
>>
>>Okay.
>>
>>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-16 14:33                   ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-16 14:33 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, May 15, 2023 11:31 AM
>>
>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>
>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>
>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>> >+definitions:
>>>>>>> >+  -
>>>>>>> >+    type: enum
>>>>>>> >+    name: mode
>>>>>>> >+    doc: |
>>>>>>> >+      working-modes a dpll can support, differentiate if and how dpll
>>>>>>>selects
>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>DPLL_A_MODE
>>>>>>> >+      attribute
>>>>>>> >+    entries:
>>>>>>> >+      -
>>>>>>> >+        name: unspec
>>>>>>>
>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>
>>>>>>+1
>>>>>>
>>>>>
>>>>>Sure, fixed.
>>>>>
>>>>>>> >+        doc: unspecified value
>>>>>>> >+      -
>>>>>>> >+        name: manual
>>>>>>
>>>>>>I think the documentation calls this "forced", still.
>>>>>>
>>>>>
>>>>>Yes, good catch, fixed docs.
>>>>>
>>>>>>> >+        doc: source can be only selected by sending a request to dpll
>>>>>>> >+      -
>>>>>>> >+        name: automatic
>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>> >+      -
>>>>>>> >+        name: holdover
>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>> >+      -
>>>>>>> >+        name: freerun
>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>
>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>
>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>at play here?
>>>>>
>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>seconds before it locks to a signal once it is selected.
>>>>>
>>>>>>
>>>>>>> >+      -
>>>>>>> >+        name: nco
>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>
>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>
>>>>>
>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>it
>>>>
>>>>So how exactly this is different to freerun? Does user care or he would
>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>device specific thing that should be abstracted out here?
>>>>
>>>
>>>Sure, it is device specific, some synchronizing circuits would have this
>>>capability, while others would not.
>>>Should be abstracted out? It is a good question.. shall user know that he is
>>>in
>>>freerun with possibility to control the frequency or not?
>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>supporting multiple output frequencies.
>>>How the one would know if those frequencies are supported only in
>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>In other words: As the user can I change a frequency of a dpll if active
>>>mode is FREERUN?
>>
>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>understanding that you can control frequency in NCO mode is not present
>>:/ That only means it may be confusing and not described properly.
>>How do you control this frequency exactly? I see no such knob.
>>
>
>The set frequency is there already, although we miss phase offset I guess.

Yeah, but on a pin, right?



>
>But I have changed my mind on having this in the kernel..
>Initially I have added this mode as our HW supports it, while thinking that
>dpll subsystem shall have this, and we will implement it one day..
>But as we have not implemented it yet, let's leave work and discussion on
>this mode for the future, when someone will actually try to implement it.

Yeah, let's drop it then. One less confusing thing to wrap a head around :)


>
>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>but locked this "internal pin"? We know how to control frequency there.
>>
>
>Hmm, yeah probably could work this way.
>
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>I would say it is better to have such mode, we could argue on naming though.
>>>
>>>>
>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>dividers before it reaches the output).
>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>SYSTEM CLOCK.
>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>
>>>>>>> >+    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: unspec
>>>>>>> >+        doc: unspecified value
>>>>>>> >+      -
>>>>>>> >+        name: unlocked
>>>>>>> >+        doc: |
>>>>>>> >+          dpll was not yet locked to any valid source (or is in one of
>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>> >+      -
>>>>>>> >+        name: calibrating
>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>> >+      -
>>>>>>> >+        name: locked
>>>>>>> >+        doc: dpll is locked
>>>>>>> >+      -
>>>>>>> >+        name: holdover
>>>>>>> >+        doc: |
>>>>>>> >+          dpll is in holdover state - lost a valid lock or was forced by
>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>
>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>
>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>lock status when request reached the HW.
>>>>>>
>>>>>
>>>>>Improved the docs:
>>>>>        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 it was not, the dpll's lock-status will remain
>>>>
>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>you iron-out the phrasing a bit please?
>>>
>>>
>>>Hmmm,
>>>        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)
>>>
>>>Hope this is better?
>>
>>Okay.
>>
>>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-04-28  0:20 ` Vadim Fedorenko
@ 2023-05-17 10:18   ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-17 10:18 UTC (permalink / raw)
  To: Vadim Fedorenko, Arkadiusz Kubalewski
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Milena Olech,
	Michal Michalik, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

Let me summarize the outcome of the discussion between me and Jakub
regarding attributes, handles and ID lookups in the RFCv7 thread:

--------------------------------------------------------------
** Needed changes for RFCv8 **

1) No scoped indexes.
   The indexes passed from driver to dpll core during call of:
        dpll_device_get() - device_idx
        dpll_pin_get() - pin_idx
   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
   Therefore following attributes need to be removed:
   DPLL_A_PIN_IDX
   DPLL_A_PIN_PARENT_IDX

2) For device, the handle will be DPLL_A_ID == dpll->id.
   This will be the only handle for device for every
   device related GET, SET command and every device related notification.
   Note: this ID is not deterministing and may be different depending on
   order of device probes etc.

3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
   This will be the only handle for pin for every
   pin related GET, SET command and every pin related notification.
   Note: this ID is not deterministing and may be different depending on
   order of device probes etc.

4) Remove attribute:
   DPLL_A_PIN_LABEL
   and replace it with:
   DPLL_A_PIN_PANEL_LABEL (string)
   DPLL_A_PIN_XXX (string)
   where XXX is a label type, like for example:
     DPLL_A_PIN_BOARD_LABEL
     DPLL_A_PIN_BOARD_TRACE
     DPLL_A_PIN_PACKAGE_PIN

5) Make sure you expose following attributes for every device and
   pin GET/DUMP command reply message:
   DPLL_A_MODULE_NAME
   DPLL_A_CLOCK_ID

6) Remove attributes:
   DPLL_A_DEV_NAME
   DPLL_A_BUS_NAME
   as they no longer have any value and do no make sense (even in RFCv7)


--------------------------------------------------------------
** Lookup commands **

Basically these would allow user to query DEVICE_ID and PIN_ID
according to provided atributes (see examples below).

These would be from my perspective optional for this patchsets.
I believe we can do it as follow-up if needed. For example for mlx5
I don't have usecase for it, since I can consistently get PIN_ID
using RT netlink for given netdev. But I can imagine that for non-SyncE
dpll driver this would make sense to have.

1) Introduce CMD_GET_ID - query the kernel for a dpll device
                          specified by given attrs
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_TYPE
   <- DPLL_A_ID
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- DPLL_A_ID
   Example:
   -> DPLL_A_MODULE_NAME
   <- -EINVAL (Extack: "multiple devices matched")

   If user passes a subset of attrs which would not result in
   a single match, kernel returns -EINVAL and proper extack message.

2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
                              specified by given attrs
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_PIN_TYPE
      DPLL_A_PIN_PANEL_LABEL
   <- DPLL_A_PIN_ID
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- -EINVAL (Extack: "multiple pins matched")

   If user passes a subset of attrs which would not result in
   a single match, kernel returns -EINVAL and proper extack message.

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-17 10:18   ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-17 10:18 UTC (permalink / raw)
  To: Vadim Fedorenko, Arkadiusz Kubalewski
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Milena Olech,
	Michal Michalik, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

Let me summarize the outcome of the discussion between me and Jakub
regarding attributes, handles and ID lookups in the RFCv7 thread:

--------------------------------------------------------------
** Needed changes for RFCv8 **

1) No scoped indexes.
   The indexes passed from driver to dpll core during call of:
        dpll_device_get() - device_idx
        dpll_pin_get() - pin_idx
   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
   Therefore following attributes need to be removed:
   DPLL_A_PIN_IDX
   DPLL_A_PIN_PARENT_IDX

2) For device, the handle will be DPLL_A_ID == dpll->id.
   This will be the only handle for device for every
   device related GET, SET command and every device related notification.
   Note: this ID is not deterministing and may be different depending on
   order of device probes etc.

3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
   This will be the only handle for pin for every
   pin related GET, SET command and every pin related notification.
   Note: this ID is not deterministing and may be different depending on
   order of device probes etc.

4) Remove attribute:
   DPLL_A_PIN_LABEL
   and replace it with:
   DPLL_A_PIN_PANEL_LABEL (string)
   DPLL_A_PIN_XXX (string)
   where XXX is a label type, like for example:
     DPLL_A_PIN_BOARD_LABEL
     DPLL_A_PIN_BOARD_TRACE
     DPLL_A_PIN_PACKAGE_PIN

5) Make sure you expose following attributes for every device and
   pin GET/DUMP command reply message:
   DPLL_A_MODULE_NAME
   DPLL_A_CLOCK_ID

6) Remove attributes:
   DPLL_A_DEV_NAME
   DPLL_A_BUS_NAME
   as they no longer have any value and do no make sense (even in RFCv7)


--------------------------------------------------------------
** Lookup commands **

Basically these would allow user to query DEVICE_ID and PIN_ID
according to provided atributes (see examples below).

These would be from my perspective optional for this patchsets.
I believe we can do it as follow-up if needed. For example for mlx5
I don't have usecase for it, since I can consistently get PIN_ID
using RT netlink for given netdev. But I can imagine that for non-SyncE
dpll driver this would make sense to have.

1) Introduce CMD_GET_ID - query the kernel for a dpll device
                          specified by given attrs
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_TYPE
   <- DPLL_A_ID
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- DPLL_A_ID
   Example:
   -> DPLL_A_MODULE_NAME
   <- -EINVAL (Extack: "multiple devices matched")

   If user passes a subset of attrs which would not result in
   a single match, kernel returns -EINVAL and proper extack message.

2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
                              specified by given attrs
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_PIN_TYPE
      DPLL_A_PIN_PANEL_LABEL
   <- DPLL_A_PIN_ID
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
   Example:
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
   <- -EINVAL (Extack: "multiple pins matched")

   If user passes a subset of attrs which would not result in
   a single match, kernel returns -EINVAL and proper extack message.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-16 14:33                   ` Jiri Pirko
@ 2023-05-18 13:24                     ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 13:24 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 4:34 PM
>
>Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, May 15, 2023 11:31 AM
>>>
>>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>>
>>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>>
>>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>>> >+definitions:
>>>>>>>> >+  -
>>>>>>>> >+    type: enum
>>>>>>>> >+    name: mode
>>>>>>>> >+    doc: |
>>>>>>>> >+      working-modes a dpll can support, differentiate if and how
>>>>>>>> >dpll selects
>>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>> >DPLL_A_MODE
>>>>>>>> >+      attribute
>>>>>>>> >+    entries:
>>>>>>>> >+      -
>>>>>>>> >+        name: unspec
>>>>>>>>
>>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>>
>>>>>>>+1
>>>>>>>
>>>>>>
>>>>>>Sure, fixed.
>>>>>>
>>>>>>>> >+        doc: unspecified value
>>>>>>>> >+      -
>>>>>>>> >+        name: manual
>>>>>>>
>>>>>>>I think the documentation calls this "forced", still.
>>>>>>>
>>>>>>
>>>>>>Yes, good catch, fixed docs.
>>>>>>
>>>>>>>> >+        doc: source can be only selected by sending a request to
>>>>>>>> >dpll
>>>>>>>> >+      -
>>>>>>>> >+        name: automatic
>>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>>> >+      -
>>>>>>>> >+        name: holdover
>>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>>> >+      -
>>>>>>>> >+        name: freerun
>>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>>
>>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>>
>>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>>at play here?
>>>>>>
>>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>>seconds before it locks to a signal once it is selected.
>>>>>>
>>>>>>>
>>>>>>>> >+      -
>>>>>>>> >+        name: nco
>>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>>
>>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>>
>>>>>>
>>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>>it
>>>>>
>>>>>So how exactly this is different to freerun? Does user care or he would
>>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>>device specific thing that should be abstracted out here?
>>>>>
>>>>
>>>>Sure, it is device specific, some synchronizing circuits would have this
>>>>capability, while others would not.
>>>>Should be abstracted out? It is a good question.. shall user know that he
>>>>is in
>>>>freerun with possibility to control the frequency or not?
>>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>>supporting multiple output frequencies.
>>>>How the one would know if those frequencies are supported only in
>>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>>In other words: As the user can I change a frequency of a dpll if active
>>>>mode is FREERUN?
>>>
>>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>>understanding that you can control frequency in NCO mode is not present
>>>:/ That only means it may be confusing and not described properly.
>>>How do you control this frequency exactly? I see no such knob.
>>>
>>
>>The set frequency is there already, although we miss phase offset I guess.
>
>Yeah, but on a pin, right?
>
>

Yes frequency of an output pin is configurable, phase offset for a dpll or
output is not there, we might think of adding it..

>
>>
>>But I have changed my mind on having this in the kernel..
>>Initially I have added this mode as our HW supports it, while thinking that
>>dpll subsystem shall have this, and we will implement it one day..
>>But as we have not implemented it yet, let's leave work and discussion on
>>this mode for the future, when someone will actually try to implement it.
>
>Yeah, let's drop it then. One less confusing thing to wrap a head around :)
>

Dropped.

Thank you!
Arkadiusz

>
>>
>>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>>but locked this "internal pin"? We know how to control frequency there.
>>>
>>
>>Hmm, yeah probably could work this way.
>>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>I would say it is better to have such mode, we could argue on naming
>>>>>though.
>>>>
>>>>>
>>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>>dividers before it reaches the output).
>>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>>SYSTEM CLOCK.
>>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>>
>>>>>>>> >+    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: unspec
>>>>>>>> >+        doc: unspecified value
>>>>>>>> >+      -
>>>>>>>> >+        name: unlocked
>>>>>>>> >+        doc: |
>>>>>>>> >+          dpll was not yet locked to any valid source (or is in one
>>>>>>>> >of
>>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>>> >+      -
>>>>>>>> >+        name: calibrating
>>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>>> >+      -
>>>>>>>> >+        name: locked
>>>>>>>> >+        doc: dpll is locked
>>>>>>>> >+      -
>>>>>>>> >+        name: holdover
>>>>>>>> >+        doc: |
>>>>>>>> >+          dpll is in holdover state - lost a valid lock or was
>>>>>>>> >forced by
>>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>>
>>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>>
>>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>>lock status when request reached the HW.
>>>>>>>
>>>>>>
>>>>>>Improved the docs:
>>>>>>        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 it was not, the dpll's lock-status will remain
>>>>>
>>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>>you iron-out the phrasing a bit please?
>>>>
>>>>
>>>>Hmmm,
>>>>        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)
>>>>
>>>>Hope this is better?
>>>
>>>Okay.
>>>
>>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-18 13:24                     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 13:24 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 4:34 PM
>
>Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, May 15, 2023 11:31 AM
>>>
>>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>>
>>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>>
>>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>>> >+definitions:
>>>>>>>> >+  -
>>>>>>>> >+    type: enum
>>>>>>>> >+    name: mode
>>>>>>>> >+    doc: |
>>>>>>>> >+      working-modes a dpll can support, differentiate if and how
>>>>>>>> >dpll selects
>>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>> >DPLL_A_MODE
>>>>>>>> >+      attribute
>>>>>>>> >+    entries:
>>>>>>>> >+      -
>>>>>>>> >+        name: unspec
>>>>>>>>
>>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>>
>>>>>>>+1
>>>>>>>
>>>>>>
>>>>>>Sure, fixed.
>>>>>>
>>>>>>>> >+        doc: unspecified value
>>>>>>>> >+      -
>>>>>>>> >+        name: manual
>>>>>>>
>>>>>>>I think the documentation calls this "forced", still.
>>>>>>>
>>>>>>
>>>>>>Yes, good catch, fixed docs.
>>>>>>
>>>>>>>> >+        doc: source can be only selected by sending a request to
>>>>>>>> >dpll
>>>>>>>> >+      -
>>>>>>>> >+        name: automatic
>>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>>> >+      -
>>>>>>>> >+        name: holdover
>>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>>> >+      -
>>>>>>>> >+        name: freerun
>>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>>
>>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>>
>>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>>at play here?
>>>>>>
>>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>>seconds before it locks to a signal once it is selected.
>>>>>>
>>>>>>>
>>>>>>>> >+      -
>>>>>>>> >+        name: nco
>>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>>
>>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>>
>>>>>>
>>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>>it
>>>>>
>>>>>So how exactly this is different to freerun? Does user care or he would
>>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>>device specific thing that should be abstracted out here?
>>>>>
>>>>
>>>>Sure, it is device specific, some synchronizing circuits would have this
>>>>capability, while others would not.
>>>>Should be abstracted out? It is a good question.. shall user know that he
>>>>is in
>>>>freerun with possibility to control the frequency or not?
>>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>>supporting multiple output frequencies.
>>>>How the one would know if those frequencies are supported only in
>>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>>In other words: As the user can I change a frequency of a dpll if active
>>>>mode is FREERUN?
>>>
>>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>>understanding that you can control frequency in NCO mode is not present
>>>:/ That only means it may be confusing and not described properly.
>>>How do you control this frequency exactly? I see no such knob.
>>>
>>
>>The set frequency is there already, although we miss phase offset I guess.
>
>Yeah, but on a pin, right?
>
>

Yes frequency of an output pin is configurable, phase offset for a dpll or
output is not there, we might think of adding it..

>
>>
>>But I have changed my mind on having this in the kernel..
>>Initially I have added this mode as our HW supports it, while thinking that
>>dpll subsystem shall have this, and we will implement it one day..
>>But as we have not implemented it yet, let's leave work and discussion on
>>this mode for the future, when someone will actually try to implement it.
>
>Yeah, let's drop it then. One less confusing thing to wrap a head around :)
>

Dropped.

Thank you!
Arkadiusz

>
>>
>>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>>but locked this "internal pin"? We know how to control frequency there.
>>>
>>
>>Hmm, yeah probably could work this way.
>>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>I would say it is better to have such mode, we could argue on naming
>>>>>though.
>>>>
>>>>>
>>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>>dividers before it reaches the output).
>>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>>SYSTEM CLOCK.
>>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>>
>>>>>>>> >+    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: unspec
>>>>>>>> >+        doc: unspecified value
>>>>>>>> >+      -
>>>>>>>> >+        name: unlocked
>>>>>>>> >+        doc: |
>>>>>>>> >+          dpll was not yet locked to any valid source (or is in one
>>>>>>>> >of
>>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>>> >+      -
>>>>>>>> >+        name: calibrating
>>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>>> >+      -
>>>>>>>> >+        name: locked
>>>>>>>> >+        doc: dpll is locked
>>>>>>>> >+      -
>>>>>>>> >+        name: holdover
>>>>>>>> >+        doc: |
>>>>>>>> >+          dpll is in holdover state - lost a valid lock or was
>>>>>>>> >forced by
>>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>>
>>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>>
>>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>>lock status when request reached the HW.
>>>>>>>
>>>>>>
>>>>>>Improved the docs:
>>>>>>        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 it was not, the dpll's lock-status will remain
>>>>>
>>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>>you iron-out the phrasing a bit please?
>>>>
>>>>
>>>>Hmmm,
>>>>        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)
>>>>
>>>>Hope this is better?
>>>
>>>Okay.
>>>
>>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
  2023-05-18 13:24                     ` Kubalewski, Arkadiusz
@ 2023-05-18 14:02                       ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-18 14:02 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 18, 2023 at 03:24:45PM CEST, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 4:34 PM
>>
>>Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, May 15, 2023 11:31 AM
>>>>
>>>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>>>
>>>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>>>
>>>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>>>> >+definitions:
>>>>>>>>> >+  -
>>>>>>>>> >+    type: enum
>>>>>>>>> >+    name: mode
>>>>>>>>> >+    doc: |
>>>>>>>>> >+      working-modes a dpll can support, differentiate if and how
>>>>>>>>> >dpll selects
>>>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>>> >DPLL_A_MODE
>>>>>>>>> >+      attribute
>>>>>>>>> >+    entries:
>>>>>>>>> >+      -
>>>>>>>>> >+        name: unspec
>>>>>>>>>
>>>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>>>
>>>>>>>>+1
>>>>>>>>
>>>>>>>
>>>>>>>Sure, fixed.
>>>>>>>
>>>>>>>>> >+        doc: unspecified value
>>>>>>>>> >+      -
>>>>>>>>> >+        name: manual
>>>>>>>>
>>>>>>>>I think the documentation calls this "forced", still.
>>>>>>>>
>>>>>>>
>>>>>>>Yes, good catch, fixed docs.
>>>>>>>
>>>>>>>>> >+        doc: source can be only selected by sending a request to
>>>>>>>>> >dpll
>>>>>>>>> >+      -
>>>>>>>>> >+        name: automatic
>>>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>>>> >+      -
>>>>>>>>> >+        name: holdover
>>>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>>>> >+      -
>>>>>>>>> >+        name: freerun
>>>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>>>
>>>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>>>
>>>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>>>at play here?
>>>>>>>
>>>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>>>seconds before it locks to a signal once it is selected.
>>>>>>>
>>>>>>>>
>>>>>>>>> >+      -
>>>>>>>>> >+        name: nco
>>>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>>>
>>>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>>>
>>>>>>>
>>>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>>>it
>>>>>>
>>>>>>So how exactly this is different to freerun? Does user care or he would
>>>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>>>device specific thing that should be abstracted out here?
>>>>>>
>>>>>
>>>>>Sure, it is device specific, some synchronizing circuits would have this
>>>>>capability, while others would not.
>>>>>Should be abstracted out? It is a good question.. shall user know that he
>>>>>is in
>>>>>freerun with possibility to control the frequency or not?
>>>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>>>supporting multiple output frequencies.
>>>>>How the one would know if those frequencies are supported only in
>>>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>>>In other words: As the user can I change a frequency of a dpll if active
>>>>>mode is FREERUN?
>>>>
>>>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>>>understanding that you can control frequency in NCO mode is not present
>>>>:/ That only means it may be confusing and not described properly.
>>>>How do you control this frequency exactly? I see no such knob.
>>>>
>>>
>>>The set frequency is there already, although we miss phase offset I guess.
>>
>>Yeah, but on a pin, right?
>>
>>
>
>Yes frequency of an output pin is configurable, phase offset for a dpll or
>output is not there, we might think of adding it..

But wait, we are talking about controllig freq of NCO, that has to be a
different knob. Anyway, it is dropped, so the discussion is poitless
now.



>
>>
>>>
>>>But I have changed my mind on having this in the kernel..
>>>Initially I have added this mode as our HW supports it, while thinking that
>>>dpll subsystem shall have this, and we will implement it one day..
>>>But as we have not implemented it yet, let's leave work and discussion on
>>>this mode for the future, when someone will actually try to implement it.
>>
>>Yeah, let's drop it then. One less confusing thing to wrap a head around :)
>>
>
>Dropped.
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>>>but locked this "internal pin"? We know how to control frequency there.
>>>>
>>>
>>>Hmm, yeah probably could work this way.
>>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>I would say it is better to have such mode, we could argue on naming
>>>>>>though.
>>>>>
>>>>>>
>>>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>>>dividers before it reaches the output).
>>>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>>>SYSTEM CLOCK.
>>>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>>>
>>>>>>>>> >+    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: unspec
>>>>>>>>> >+        doc: unspecified value
>>>>>>>>> >+      -
>>>>>>>>> >+        name: unlocked
>>>>>>>>> >+        doc: |
>>>>>>>>> >+          dpll was not yet locked to any valid source (or is in one
>>>>>>>>> >of
>>>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>>>> >+      -
>>>>>>>>> >+        name: calibrating
>>>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>>>> >+      -
>>>>>>>>> >+        name: locked
>>>>>>>>> >+        doc: dpll is locked
>>>>>>>>> >+      -
>>>>>>>>> >+        name: holdover
>>>>>>>>> >+        doc: |
>>>>>>>>> >+          dpll is in holdover state - lost a valid lock or was
>>>>>>>>> >forced by
>>>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>>>
>>>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>>>
>>>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>>>lock status when request reached the HW.
>>>>>>>>
>>>>>>>
>>>>>>>Improved the docs:
>>>>>>>        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 it was not, the dpll's lock-status will remain
>>>>>>
>>>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>>>you iron-out the phrasing a bit please?
>>>>>
>>>>>
>>>>>Hmmm,
>>>>>        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)
>>>>>
>>>>>Hope this is better?
>>>>
>>>>Okay.
>>>>
>>>>>
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML
@ 2023-05-18 14:02                       ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-18 14:02 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Vadim Fedorenko

Thu, May 18, 2023 at 03:24:45PM CEST, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 4:34 PM
>>
>>Tue, May 16, 2023 at 02:05:38PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, May 15, 2023 11:31 AM
>>>>
>>>>Thu, May 11, 2023 at 10:51:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, May 11, 2023 10:00 AM
>>>>>>
>>>>>>Thu, May 11, 2023 at 09:40:26AM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jakub Kicinski <kuba@kernel.org>
>>>>>>>>Sent: Thursday, May 4, 2023 11:25 PM
>>>>>>>>
>>>>>>>>On Thu, 4 May 2023 14:02:30 +0200 Jiri Pirko wrote:
>>>>>>>>> >+definitions:
>>>>>>>>> >+  -
>>>>>>>>> >+    type: enum
>>>>>>>>> >+    name: mode
>>>>>>>>> >+    doc: |
>>>>>>>>> >+      working-modes a dpll can support, differentiate if and how
>>>>>>>>> >dpll selects
>>>>>>>>> >+      one of its sources to syntonize with it, valid values for
>>>>>>>>> >DPLL_A_MODE
>>>>>>>>> >+      attribute
>>>>>>>>> >+    entries:
>>>>>>>>> >+      -
>>>>>>>>> >+        name: unspec
>>>>>>>>>
>>>>>>>>> In general, why exactly do we need unspec values in enums and CMDs?
>>>>>>>>> What is the usecase. If there isn't please remove.
>>>>>>>>
>>>>>>>>+1
>>>>>>>>
>>>>>>>
>>>>>>>Sure, fixed.
>>>>>>>
>>>>>>>>> >+        doc: unspecified value
>>>>>>>>> >+      -
>>>>>>>>> >+        name: manual
>>>>>>>>
>>>>>>>>I think the documentation calls this "forced", still.
>>>>>>>>
>>>>>>>
>>>>>>>Yes, good catch, fixed docs.
>>>>>>>
>>>>>>>>> >+        doc: source can be only selected by sending a request to
>>>>>>>>> >dpll
>>>>>>>>> >+      -
>>>>>>>>> >+        name: automatic
>>>>>>>>> >+        doc: highest prio, valid source, auto selected by dpll
>>>>>>>>> >+      -
>>>>>>>>> >+        name: holdover
>>>>>>>>> >+        doc: dpll forced into holdover mode
>>>>>>>>> >+      -
>>>>>>>>> >+        name: freerun
>>>>>>>>> >+        doc: dpll driven on system clk, no holdover available
>>>>>>>>>
>>>>>>>>> Remove "no holdover available". This is not a state, this is a mode
>>>>>>>>> configuration. If holdover is or isn't available, is a runtime info.
>>>>>>>>
>>>>>>>>Agreed, seems a little confusing now. Should we expose the system clk
>>>>>>>>as a pin to be able to force lock to it? Or there's some extra magic
>>>>>>>>at play here?
>>>>>>>
>>>>>>>In freerun you cannot lock to anything it, it just uses system clock from
>>>>>>>one of designated chip wires (which is not a part of source pins pool) to
>>>>>>>feed the dpll. Dpll would only stabilize that signal and pass it further.
>>>>>>>Locking itself is some kind of magic, as it usually takes at least ~15
>>>>>>>seconds before it locks to a signal once it is selected.
>>>>>>>
>>>>>>>>
>>>>>>>>> >+      -
>>>>>>>>> >+        name: nco
>>>>>>>>> >+        doc: dpll driven by Numerically Controlled Oscillator
>>>>>>>>
>>>>>>>>Noob question, what is NCO in terms of implementation?
>>>>>>>>We source the signal from an arbitrary pin and FW / driver does
>>>>>>>>the control? Or we always use system refclk and then tune?
>>>>>>>>
>>>>>>>
>>>>>>>Documentation of chip we are using, stated NCO as similar to FREERUN, and
>>>>>>>it
>>>>>>
>>>>>>So how exactly this is different to freerun? Does user care or he would
>>>>>>be fine with "freerun" in this case? My point is, isn't "NCO" some
>>>>>>device specific thing that should be abstracted out here?
>>>>>>
>>>>>
>>>>>Sure, it is device specific, some synchronizing circuits would have this
>>>>>capability, while others would not.
>>>>>Should be abstracted out? It is a good question.. shall user know that he
>>>>>is in
>>>>>freerun with possibility to control the frequency or not?
>>>>>Let's say we remove NCO, and have dpll with enabled FREERUN mode and pins
>>>>>supporting multiple output frequencies.
>>>>>How the one would know if those frequencies are supported only in
>>>>>MANUAL/AUTOMATIC modes or also in the FREERUN mode?
>>>>>In other words: As the user can I change a frequency of a dpll if active
>>>>>mode is FREERUN?
>>>>
>>>>Okay, I think I'm deep in the DPLL infra you are pushing, but my
>>>>understanding that you can control frequency in NCO mode is not present
>>>>:/ That only means it may be confusing and not described properly.
>>>>How do you control this frequency exactly? I see no such knob.
>>>>
>>>
>>>The set frequency is there already, although we miss phase offset I guess.
>>
>>Yeah, but on a pin, right?
>>
>>
>
>Yes frequency of an output pin is configurable, phase offset for a dpll or
>output is not there, we might think of adding it..

But wait, we are talking about controllig freq of NCO, that has to be a
different knob. Anyway, it is dropped, so the discussion is poitless
now.



>
>>
>>>
>>>But I have changed my mind on having this in the kernel..
>>>Initially I have added this mode as our HW supports it, while thinking that
>>>dpll subsystem shall have this, and we will implement it one day..
>>>But as we have not implemented it yet, let's leave work and discussion on
>>>this mode for the future, when someone will actually try to implement it.
>>
>>Yeah, let's drop it then. One less confusing thing to wrap a head around :)
>>
>
>Dropped.
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>>Can't the oscilator be modeled as a pin and then you are not in freerun
>>>>but locked this "internal pin"? We know how to control frequency there.
>>>>
>>>
>>>Hmm, yeah probably could work this way.
>>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>I would say it is better to have such mode, we could argue on naming
>>>>>>though.
>>>>>
>>>>>>
>>>>>>>runs on a SYSTEM CLOCK provided to the chip (plus some stabilization and
>>>>>>>dividers before it reaches the output).
>>>>>>>It doesn't count as an source pin, it uses signal form dedicated wire for
>>>>>>>SYSTEM CLOCK.
>>>>>>>In this case control over output frequency is done by synchronizer chip
>>>>>>>firmware, but still it will not lock to any source pin signal.
>>>>>>>
>>>>>>>>> >+    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: unspec
>>>>>>>>> >+        doc: unspecified value
>>>>>>>>> >+      -
>>>>>>>>> >+        name: unlocked
>>>>>>>>> >+        doc: |
>>>>>>>>> >+          dpll was not yet locked to any valid source (or is in one
>>>>>>>>> >of
>>>>>>>>> >+          modes: DPLL_MODE_FREERUN, DPLL_MODE_NCO)
>>>>>>>>> >+      -
>>>>>>>>> >+        name: calibrating
>>>>>>>>> >+        doc: dpll is trying to lock to a valid signal
>>>>>>>>> >+      -
>>>>>>>>> >+        name: locked
>>>>>>>>> >+        doc: dpll is locked
>>>>>>>>> >+      -
>>>>>>>>> >+        name: holdover
>>>>>>>>> >+        doc: |
>>>>>>>>> >+          dpll is in holdover state - lost a valid lock or was
>>>>>>>>> >forced by
>>>>>>>>> >+          selecting DPLL_MODE_HOLDOVER mode
>>>>>>>>>
>>>>>>>>> Is it needed to mention the holdover mode. It's slightly confusing,
>>>>>>>>> because user might understand that the lock-status is always "holdover"
>>>>>>>>> in case of "holdover" mode. But it could be "unlocked", can't it?
>>>>>>>>> Perhaps I don't understand the flows there correctly :/
>>>>>>>>
>>>>>>>>Hm, if we want to make sure that holdover mode must result in holdover
>>>>>>>>state then we need some extra atomicity requirements on the SET
>>>>>>>>operation. To me it seems logical enough that after setting holdover
>>>>>>>>mode we'll end up either in holdover or unlocked status, depending on
>>>>>>>>lock status when request reached the HW.
>>>>>>>>
>>>>>>>
>>>>>>>Improved the docs:
>>>>>>>        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 it was not, the dpll's lock-status will remain
>>>>>>
>>>>>>"if it was not" does not really cope with the sentence above that. Could
>>>>>>you iron-out the phrasing a bit please?
>>>>>
>>>>>
>>>>>Hmmm,
>>>>>        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)
>>>>>
>>>>>Hope this is better?
>>>>
>>>>Okay.
>>>>
>>>>>
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-16  6:26         ` Jiri Pirko
@ 2023-05-18 16:06           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 16:06 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 8:26 AM
>
>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>
>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>+ * 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
>>>>+ * @frequency: frequency to be set
>>>>+ * @extack: error reporting
>>>>+ * @pin_type: type of pin being configured
>>>>+ *
>>>>+ * Wraps internal set frequency command on a pin.
>>>>+ *
>>>>+ * 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,
>>>>+		       const u32 frequency,
>>>>+		       struct netlink_ext_ack *extack,
>>>>+		       const enum ice_dpll_pin_type pin_type) {
>>>>+	struct ice_pf *pf = pin_priv;
>>>>+	struct ice_dpll_pin *p;
>>>>+	int ret = -EINVAL;
>>>>+
>>>>+	if (!pf)
>>>>+		return ret;
>>>>+	if (ice_dpll_cb_lock(pf))
>>>>+		return -EBUSY;
>>>>+	p = ice_find_pin(pf, pin, pin_type);
>>>
>>>This does not make any sense to me. You should avoid the lookups and remove
>>>ice_find_pin() function entirely. The purpose of having pin_priv is to
>>>carry the struct ice_dpll_pin * directly. You should pass it down during
>>>pin register.
>>>
>>>pf pointer is stored in dpll_priv.
>>>
>>
>>In this case dpll_priv is not passed, so cannot use it.
>
>It should be passed. In general to every op where *dpll is passed, the
>dpll_priv pointer should be passed along. Please, fix this.
>
>
>>But in general it makes sense I will hold pf inside of ice_dpll_pin
>>and fix this.
>
>Nope, just use dpll_priv. That's why we have it.
>

Ok, will propagate it.

>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>>>+ * @dpll: dpll being configured
>>>>+ * @pin: pointer to a pin
>>>>+ * @pin_priv: private data pointer passed on pin registration
>>>>+ * @state: state of pin to be set
>>>>+ * @extack: error reporting
>>>>+ * @pin_type: type of a pin
>>>>+ *
>>>>+ * Set pin state on a pin.
>>>>+ *
>>>>+ * Return:
>>>>+ * * 0 - OK or no change required
>>>>+ * * negative - error
>>>>+ */
>>>>+static int
>>>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>>>+		       const struct dpll_pin *pin, void *pin_priv,
>>>>+		       const enum dpll_pin_state state,
>>>
>>>Why you use const with enums?
>>>
>>
>>Just show usage intention explicitly.
>
>Does not make any sense what so ever. Please avoid it.
>

Fixed.

>
>>>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>>>+					  void *pin_priv,
>>>>+					  const struct dpll_pin *parent_pin,
>>>>+					  enum dpll_pin_state *state,
>>>>+					  struct netlink_ext_ack *extack) {
>>>>+	struct ice_pf *pf = pin_priv;
>>>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>>
>>>Reverse christmas tree ordering please.
>>
>>Fixed.
>>
>>>
>>>
>>>>+	struct ice_dpll_pin *p;
>>>>+	int ret = -EFAULT;
>>>>+
>>>>+	if (!pf)
>>>
>>>How exacly this can happen. My wild guess is it can't. Don't do such
>>>pointless checks please, confuses the reader.
>>>
>>
>>From driver perspective the pf pointer value is given by external entity,
>>why shouldn't it be valdiated?
>
>What? You pass it during register, you get it back here. Nothing to
>check. Please drop it. Non-sense checks like this have no place in
>kernel, they only confuse reader as he/she assumes it is a valid case.
>

I have some background from Automotive, MISRA etc.
Removed.

>
>[...]
>
>
>>>
>>>
>>>>+			pins[i].pin = NULL;
>>>>+			return -ENOMEM;
>>>>+		}
>>>>+		if (cgu) {
>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>+						pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>+						pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>
>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>here.
>>>
>>
>>No, in case of error, the caller releases everything
>ice_dpll_release_all(..).
>
>
>How does ice_dpll_release_all() where you failed? If you need to
>unregister one or both or none? I know that in ice you have odd ways to
>handle error paths in general, but this one clearly seems to be broken.
>

It doesn't have to, as release all would release all anyway.
Leaving it for now.

>
>
>
>
>>
>>>
>>>>+		}
>>>>+	}
>>>>+	if (cgu) {
>>>>+		ops = &ice_dpll_output_ops;
>>>>+		pins = pf->dplls.outputs;
>>>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>>>+						   i + pf->dplls.num_inputs,
>>>>+						   THIS_MODULE, &pins[i].prop);
>>>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>>>+				pins[i].pin = NULL;
>>>>+				return -ENOMEM;
>>>
>>>Don't make up error values when you get them from the function you call:
>>>	return PTR_ERR(pins[i].pin);
>>
>>Fixed.
>>
>>>
>>>>+			}
>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>
>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>here.
>>>
>>
>>As above, in case of error, the caller releases everything.
>
>As above, I don't think it works.
>

It works, though not all error paths were probably test..
Will take another look on it later.

>
>[...]
>
>
>>>>+	}
>>>>+
>>>>+	if (cgu) {
>>>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>>>+					   &ice_dpll_ops, pf, dev);
>>>>+		if (ret)
>>>>+			goto put_pps;
>>>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>>>+					   &ice_dpll_ops, pf, dev);
>>>>+		if (ret)
>>>
>>>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>>>DPLL_TYPE_EEC here. Fix the error path.
>>>
>>
>>The caller shall do the clean up, but yeah will fix this as here clean up
>>is not expected.
>
>:) Just make your error paths obvious and easy to follow to not to
>confuse anybody, you included.
>

I am not confused :) it wasn't removed as it should.

Thank you!
Arkadiusz

>
>>
>>>
>>>>+			goto put_pps;
>>>>+	}
>>>>+
>>>>+	return 0;
>>>>+
>>>>+put_pps:
>>>>+	dpll_device_put(pf->dplls.pps.dpll);
>>>>+	pf->dplls.pps.dpll = NULL;
>>>>+put_eec:
>>>>+	dpll_device_put(pf->dplls.eec.dpll);
>>>>+	pf->dplls.eec.dpll = NULL;
>>>>+
>>>>+	return ret;
>>>>+}
>
>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-18 16:06           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 16:06 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 8:26 AM
>
>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>
>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>+ * 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
>>>>+ * @frequency: frequency to be set
>>>>+ * @extack: error reporting
>>>>+ * @pin_type: type of pin being configured
>>>>+ *
>>>>+ * Wraps internal set frequency command on a pin.
>>>>+ *
>>>>+ * 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,
>>>>+		       const u32 frequency,
>>>>+		       struct netlink_ext_ack *extack,
>>>>+		       const enum ice_dpll_pin_type pin_type) {
>>>>+	struct ice_pf *pf = pin_priv;
>>>>+	struct ice_dpll_pin *p;
>>>>+	int ret = -EINVAL;
>>>>+
>>>>+	if (!pf)
>>>>+		return ret;
>>>>+	if (ice_dpll_cb_lock(pf))
>>>>+		return -EBUSY;
>>>>+	p = ice_find_pin(pf, pin, pin_type);
>>>
>>>This does not make any sense to me. You should avoid the lookups and remove
>>>ice_find_pin() function entirely. The purpose of having pin_priv is to
>>>carry the struct ice_dpll_pin * directly. You should pass it down during
>>>pin register.
>>>
>>>pf pointer is stored in dpll_priv.
>>>
>>
>>In this case dpll_priv is not passed, so cannot use it.
>
>It should be passed. In general to every op where *dpll is passed, the
>dpll_priv pointer should be passed along. Please, fix this.
>
>
>>But in general it makes sense I will hold pf inside of ice_dpll_pin
>>and fix this.
>
>Nope, just use dpll_priv. That's why we have it.
>

Ok, will propagate it.

>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>>>+ * @dpll: dpll being configured
>>>>+ * @pin: pointer to a pin
>>>>+ * @pin_priv: private data pointer passed on pin registration
>>>>+ * @state: state of pin to be set
>>>>+ * @extack: error reporting
>>>>+ * @pin_type: type of a pin
>>>>+ *
>>>>+ * Set pin state on a pin.
>>>>+ *
>>>>+ * Return:
>>>>+ * * 0 - OK or no change required
>>>>+ * * negative - error
>>>>+ */
>>>>+static int
>>>>+ice_dpll_pin_state_set(const struct dpll_device *dpll,
>>>>+		       const struct dpll_pin *pin, void *pin_priv,
>>>>+		       const enum dpll_pin_state state,
>>>
>>>Why you use const with enums?
>>>
>>
>>Just show usage intention explicitly.
>
>Does not make any sense what so ever. Please avoid it.
>

Fixed.

>
>>>>+static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>>>>+					  void *pin_priv,
>>>>+					  const struct dpll_pin *parent_pin,
>>>>+					  enum dpll_pin_state *state,
>>>>+					  struct netlink_ext_ack *extack) {
>>>>+	struct ice_pf *pf = pin_priv;
>>>>+	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>>>
>>>Reverse christmas tree ordering please.
>>
>>Fixed.
>>
>>>
>>>
>>>>+	struct ice_dpll_pin *p;
>>>>+	int ret = -EFAULT;
>>>>+
>>>>+	if (!pf)
>>>
>>>How exacly this can happen. My wild guess is it can't. Don't do such
>>>pointless checks please, confuses the reader.
>>>
>>
>>From driver perspective the pf pointer value is given by external entity,
>>why shouldn't it be valdiated?
>
>What? You pass it during register, you get it back here. Nothing to
>check. Please drop it. Non-sense checks like this have no place in
>kernel, they only confuse reader as he/she assumes it is a valid case.
>

I have some background from Automotive, MISRA etc.
Removed.

>
>[...]
>
>
>>>
>>>
>>>>+			pins[i].pin = NULL;
>>>>+			return -ENOMEM;
>>>>+		}
>>>>+		if (cgu) {
>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>+						pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>+						pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>
>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>here.
>>>
>>
>>No, in case of error, the caller releases everything
>ice_dpll_release_all(..).
>
>
>How does ice_dpll_release_all() where you failed? If you need to
>unregister one or both or none? I know that in ice you have odd ways to
>handle error paths in general, but this one clearly seems to be broken.
>

It doesn't have to, as release all would release all anyway.
Leaving it for now.

>
>
>
>
>>
>>>
>>>>+		}
>>>>+	}
>>>>+	if (cgu) {
>>>>+		ops = &ice_dpll_output_ops;
>>>>+		pins = pf->dplls.outputs;
>>>>+		for (i = 0; i < pf->dplls.num_outputs; i++) {
>>>>+			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>>>>+						   i + pf->dplls.num_inputs,
>>>>+						   THIS_MODULE, &pins[i].prop);
>>>>+			if (IS_ERR_OR_NULL(pins[i].pin)) {
>>>>+				pins[i].pin = NULL;
>>>>+				return -ENOMEM;
>>>
>>>Don't make up error values when you get them from the function you call:
>>>	return PTR_ERR(pins[i].pin);
>>
>>Fixed.
>>
>>>
>>>>+			}
>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
>>>>+						ops, pf, NULL);
>>>>+			if (ret)
>>>>+				return ret;
>>>
>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>here.
>>>
>>
>>As above, in case of error, the caller releases everything.
>
>As above, I don't think it works.
>

It works, though not all error paths were probably test..
Will take another look on it later.

>
>[...]
>
>
>>>>+	}
>>>>+
>>>>+	if (cgu) {
>>>>+		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
>>>>+					   &ice_dpll_ops, pf, dev);
>>>>+		if (ret)
>>>>+			goto put_pps;
>>>>+		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
>>>>+					   &ice_dpll_ops, pf, dev);
>>>>+		if (ret)
>>>
>>>You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>>>DPLL_TYPE_EEC here. Fix the error path.
>>>
>>
>>The caller shall do the clean up, but yeah will fix this as here clean up
>>is not expected.
>
>:) Just make your error paths obvious and easy to follow to not to
>confuse anybody, you included.
>

I am not confused :) it wasn't removed as it should.

Thank you!
Arkadiusz

>
>>
>>>
>>>>+			goto put_pps;
>>>>+	}
>>>>+
>>>>+	return 0;
>>>>+
>>>>+put_pps:
>>>>+	dpll_device_put(pf->dplls.pps.dpll);
>>>>+	pf->dplls.pps.dpll = NULL;
>>>>+put_eec:
>>>>+	dpll_device_put(pf->dplls.eec.dpll);
>>>>+	pf->dplls.eec.dpll = NULL;
>>>>+
>>>>+	return ret;
>>>>+}
>
>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-16 11:46         ` Jiri Pirko
@ 2023-05-18 16:07           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 16:07 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 1:47 PM
>
>Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, May 15, 2023 7:13 PM
>>>
>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>
>>>[...]
>>>
>>>>+static const enum dpll_lock_status
>>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>>
>>>This is a bit confusing to me. You are locked, yet you report
>>>calibrating? Wouldn't it be better to have:
>>>DPLL_LOCK_STATUS_LOCKED
>>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>>
>>>?
>>>
>>
>>Sure makes sense, will add this state.
>
>Do you need "calibrating" then? I mean, the docs says:
>  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
>                                source pin signal
>
>Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
>Seems like you should have:
>[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
>[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>
>and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?
>
>Also, as a sidenote, could you use the whole names of enum value names
>in documentation? Simple reason, greppability.
>

Yes, removed CALIBRATING.
Fixed the docs.

Thank you!
Arkadiusz

>Thanks!
>
>
>>
>>>
>>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>>+};
>>>
>>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-18 16:07           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-18 16:07 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 16, 2023 1:47 PM
>
>Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, May 15, 2023 7:13 PM
>>>
>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>
>>>[...]
>>>
>>>>+static const enum dpll_lock_status
>>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>>
>>>This is a bit confusing to me. You are locked, yet you report
>>>calibrating? Wouldn't it be better to have:
>>>DPLL_LOCK_STATUS_LOCKED
>>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>>
>>>?
>>>
>>
>>Sure makes sense, will add this state.
>
>Do you need "calibrating" then? I mean, the docs says:
>  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
>                                source pin signal
>
>Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
>Seems like you should have:
>[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
>[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>
>and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?
>
>Also, as a sidenote, could you use the whole names of enum value names
>in documentation? Simple reason, greppability.
>

Yes, removed CALIBRATING.
Fixed the docs.

Thank you!
Arkadiusz

>Thanks!
>
>
>>
>>>
>>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>>+};
>>>
>>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-18 16:06           ` Kubalewski, Arkadiusz
@ 2023-05-19  6:15             ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-19  6:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Thu, May 18, 2023 at 06:06:03PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 8:26 AM
>>
>>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>>
>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>>>+			pins[i].pin = NULL;
>>>>>+			return -ENOMEM;
>>>>>+		}
>>>>>+		if (cgu) {
>>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>>+						pins[i].pin,
>>>>>+						ops, pf, NULL);
>>>>>+			if (ret)
>>>>>+				return ret;
>>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>>+						pins[i].pin,
>>>>>+						ops, pf, NULL);
>>>>>+			if (ret)
>>>>>+				return ret;
>>>>
>>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>>here.
>>>>
>>>
>>>No, in case of error, the caller releases everything
>>ice_dpll_release_all(..).
>>
>>
>>How does ice_dpll_release_all() where you failed? If you need to
>>unregister one or both or none? I know that in ice you have odd ways to
>>handle error paths in general, but this one clearly seems to be broken.
>>
>
>It doesn't have to, as release all would release all anyway.
>Leaving it for now.

So you call dpll_pin_unregister() even for the pin that was not
registered before? How is that even remotely correct?

Fix your error paths, please. I don't understand the resistance here :)

[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-19  6:15             ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-19  6:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Thu, May 18, 2023 at 06:06:03PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 8:26 AM
>>
>>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>>
>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>>>+			pins[i].pin = NULL;
>>>>>+			return -ENOMEM;
>>>>>+		}
>>>>>+		if (cgu) {
>>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>>+						pins[i].pin,
>>>>>+						ops, pf, NULL);
>>>>>+			if (ret)
>>>>>+				return ret;
>>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>>+						pins[i].pin,
>>>>>+						ops, pf, NULL);
>>>>>+			if (ret)
>>>>>+				return ret;
>>>>
>>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>>>>here.
>>>>
>>>
>>>No, in case of error, the caller releases everything
>>ice_dpll_release_all(..).
>>
>>
>>How does ice_dpll_release_all() where you failed? If you need to
>>unregister one or both or none? I know that in ice you have odd ways to
>>handle error paths in general, but this one clearly seems to be broken.
>>
>
>It doesn't have to, as release all would release all anyway.
>Leaving it for now.

So you call dpll_pin_unregister() even for the pin that was not
registered before? How is that even remotely correct?

Fix your error paths, please. I don't understand the resistance here :)

[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-18 16:07           ` Kubalewski, Arkadiusz
@ 2023-05-19  6:15             ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-19  6:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Thu, May 18, 2023 at 06:07:33PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 1:47 PM
>>
>>Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, May 15, 2023 7:13 PM
>>>>
>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>
>>>>[...]
>>>>
>>>>>+static const enum dpll_lock_status
>>>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>>>
>>>>This is a bit confusing to me. You are locked, yet you report
>>>>calibrating? Wouldn't it be better to have:
>>>>DPLL_LOCK_STATUS_LOCKED
>>>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>>>
>>>>?
>>>>
>>>
>>>Sure makes sense, will add this state.
>>
>>Do you need "calibrating" then? I mean, the docs says:
>>  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
>>                                source pin signal
>>
>>Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
>>Seems like you should have:
>>[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
>>[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>>
>>and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?
>>
>>Also, as a sidenote, could you use the whole names of enum value names
>>in documentation? Simple reason, greppability.
>>
>
>Yes, removed CALIBRATING.
>Fixed the docs.

Cool, thanks!


>
>Thank you!
>Arkadiusz
>
>>Thanks!
>>
>>
>>>
>>>>
>>>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>>>+};
>>>>
>>>>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-19  6:15             ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-19  6:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

Thu, May 18, 2023 at 06:07:33PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 16, 2023 1:47 PM
>>
>>Tue, May 16, 2023 at 11:22:37AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, May 15, 2023 7:13 PM
>>>>
>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>
>>>>[...]
>>>>
>>>>>+static const enum dpll_lock_status
>>>>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>>>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>>>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>>>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>>>
>>>>This is a bit confusing to me. You are locked, yet you report
>>>>calibrating? Wouldn't it be better to have:
>>>>DPLL_LOCK_STATUS_LOCKED
>>>>DPLL_LOCK_STATUS_LOCKED_HO_ACQ
>>>>
>>>>?
>>>>
>>>
>>>Sure makes sense, will add this state.
>>
>>Do you need "calibrating" then? I mean, the docs says:
>>  ``LOCK_STATUS_CALIBRATING``   dpll device calibrates to lock to the
>>                                source pin signal
>>
>>Yet you do: [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING
>>Seems like you should have:
>>[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED
>>[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>>
>>and remove DPLL_LOCK_STATUS_CALIBRATING as it would be unused?
>>
>>Also, as a sidenote, could you use the whole names of enum value names
>>in documentation? Simple reason, greppability.
>>
>
>Yes, removed CALIBRATING.
>Fixed the docs.

Cool, thanks!


>
>Thank you!
>Arkadiusz
>
>>Thanks!
>>
>>
>>>
>>>>
>>>>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>>>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>>>>+};
>>>>
>>>>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-16  6:26         ` Jiri Pirko
@ 2023-05-19  6:47           ` Paolo Abeni
  -1 siblings, 0 replies; 149+ messages in thread
From: Paolo Abeni @ 2023-05-19  6:47 UTC (permalink / raw)
  To: Jiri Pirko, Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

On Tue, 2023-05-16 at 08:26 +0200, Jiri Pirko wrote:
> Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
> > > From: Jiri Pirko <jiri@resnulli.us>
> > > Sent: Wednesday, May 3, 2023 2:19 PM
> > > 
> > > Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
> > > > From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> 
> [...]
> 
> 
> > > > + * 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
> > > > + * @frequency: frequency to be set
> > > > + * @extack: error reporting
> > > > + * @pin_type: type of pin being configured
> > > > + *
> > > > + * Wraps internal set frequency command on a pin.
> > > > + *
> > > > + * 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,
> > > > +		       const u32 frequency,
> > > > +		       struct netlink_ext_ack *extack,
> > > > +		       const enum ice_dpll_pin_type pin_type) {
> > > > +	struct ice_pf *pf = pin_priv;
> > > > +	struct ice_dpll_pin *p;
> > > > +	int ret = -EINVAL;
> > > > +
> > > > +	if (!pf)
> > > > +		return ret;
> > > > +	if (ice_dpll_cb_lock(pf))
> > > > +		return -EBUSY;
> > > > +	p = ice_find_pin(pf, pin, pin_type);
> > > 
> > > This does not make any sense to me. You should avoid the lookups and remove
> > > ice_find_pin() function entirely. The purpose of having pin_priv is to
> > > carry the struct ice_dpll_pin * directly. You should pass it down during
> > > pin register.
> > > 
> > > pf pointer is stored in dpll_priv.
> > > 
> > 
> > In this case dpll_priv is not passed, so cannot use it.
> 
> It should be passed. In general to every op where *dpll is passed, the
> dpll_priv pointer should be passed along. Please, fix this.
> 
> 
> > But in general it makes sense I will hold pf inside of ice_dpll_pin
> > and fix this.
> 
> Nope, just use dpll_priv. That's why we have it.
> 
> 
> [...]
> 
> 
> > > > +/**
> > > > + * ice_dpll_pin_state_set - set pin's state on dpll
> > > > + * @dpll: dpll being configured
> > > > + * @pin: pointer to a pin
> > > > + * @pin_priv: private data pointer passed on pin registration
> > > > + * @state: state of pin to be set
> > > > + * @extack: error reporting
> > > > + * @pin_type: type of a pin
> > > > + *
> > > > + * Set pin state on a pin.
> > > > + *
> > > > + * Return:
> > > > + * * 0 - OK or no change required
> > > > + * * negative - error
> > > > + */
> > > > +static int
> > > > +ice_dpll_pin_state_set(const struct dpll_device *dpll,
> > > > +		       const struct dpll_pin *pin, void *pin_priv,
> > > > +		       const enum dpll_pin_state state,
> > > 
> > > Why you use const with enums?
> > > 
> > 
> > Just show usage intention explicitly.
> 
> Does not make any sense what so ever. Please avoid it.
> 
> 
> > > > +static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
> > > > +					  void *pin_priv,
> > > > +					  const struct dpll_pin *parent_pin,
> > > > +					  enum dpll_pin_state *state,
> > > > +					  struct netlink_ext_ack *extack) {
> > > > +	struct ice_pf *pf = pin_priv;
> > > > +	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
> > > 
> > > Reverse christmas tree ordering please.
> > 
> > Fixed.
> > 
> > > 
> > > 
> > > > +	struct ice_dpll_pin *p;
> > > > +	int ret = -EFAULT;
> > > > +
> > > > +	if (!pf)
> > > 
> > > How exacly this can happen. My wild guess is it can't. Don't do such
> > > pointless checks please, confuses the reader.
> > > 
> > 
> > From driver perspective the pf pointer value is given by external entity,
> > why shouldn't it be valdiated?
> 
> What? You pass it during register, you get it back here. Nothing to
> check. Please drop it. Non-sense checks like this have no place in
> kernel, they only confuse reader as he/she assumes it is a valid case.
> 
> 
> [...]
> 
> 
> > > 
> > > 
> > > > +			pins[i].pin = NULL;
> > > > +			return -ENOMEM;
> > > > +		}
> > > > +		if (cgu) {
> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
> > > > +						pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
> > > > +						pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > 
> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
> > > here.
> > > 
> > 
> > No, in case of error, the caller releases everything ice_dpll_release_all(..).
> 
> 
> How does ice_dpll_release_all() where you failed? If you need to
> unregister one or both or none? I know that in ice you have odd ways to
> handle error paths in general, but this one clearly seems to be broken.
> 
> 
> 
> 
> 
> > 
> > > 
> > > > +		}
> > > > +	}
> > > > +	if (cgu) {
> > > > +		ops = &ice_dpll_output_ops;
> > > > +		pins = pf->dplls.outputs;
> > > > +		for (i = 0; i < pf->dplls.num_outputs; i++) {
> > > > +			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
> > > > +						   i + pf->dplls.num_inputs,
> > > > +						   THIS_MODULE, &pins[i].prop);
> > > > +			if (IS_ERR_OR_NULL(pins[i].pin)) {
> > > > +				pins[i].pin = NULL;
> > > > +				return -ENOMEM;
> > > 
> > > Don't make up error values when you get them from the function you call:
> > > 	return PTR_ERR(pins[i].pin);
> > 
> > Fixed.
> > 
> > > 
> > > > +			}
> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > 
> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
> > > here.
> > > 
> > 
> > As above, in case of error, the caller releases everything.
> 
> As above, I don't think it works.
> 
> 
> [...]
> 
> 
> > > > +	}
> > > > +
> > > > +	if (cgu) {
> > > > +		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
> > > > +					   &ice_dpll_ops, pf, dev);
> > > > +		if (ret)
> > > > +			goto put_pps;
> > > > +		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
> > > > +					   &ice_dpll_ops, pf, dev);
> > > > +		if (ret)
> > > 
> > > You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
> > > DPLL_TYPE_EEC here. Fix the error path.
> > > 
> > 
> > The caller shall do the clean up, but yeah will fix this as here clean up
> > is not expected.
> 
> :) Just make your error paths obvious and easy to follow to not to
> confuse anybody, you included.

I agree with Jiri. The error paths here and in ice_dpll_init_info() are
quite confusing and IMHO error prone.

It will get more easy toread and more consistent if every
initialization function does return an error code would leave the state
clean in case of error. That is, in case of error, such function should
cleanup all the partially allocated/initialized resources.

Note that in ice_dpll_init_info() the situation is more mixed-up as
ice_dpll_release_info() is called on most error paths, except the last
one. Memory should not leaked due to later ice_dpll_release_all(), but
it's really confusing.

Cheers,

Paolo


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-19  6:47           ` Paolo Abeni
  0 siblings, 0 replies; 149+ messages in thread
From: Paolo Abeni @ 2023-05-19  6:47 UTC (permalink / raw)
  To: Jiri Pirko, Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk

On Tue, 2023-05-16 at 08:26 +0200, Jiri Pirko wrote:
> Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com wrote:
> > > From: Jiri Pirko <jiri@resnulli.us>
> > > Sent: Wednesday, May 3, 2023 2:19 PM
> > > 
> > > Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
> > > > From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> 
> [...]
> 
> 
> > > > + * 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
> > > > + * @frequency: frequency to be set
> > > > + * @extack: error reporting
> > > > + * @pin_type: type of pin being configured
> > > > + *
> > > > + * Wraps internal set frequency command on a pin.
> > > > + *
> > > > + * 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,
> > > > +		       const u32 frequency,
> > > > +		       struct netlink_ext_ack *extack,
> > > > +		       const enum ice_dpll_pin_type pin_type) {
> > > > +	struct ice_pf *pf = pin_priv;
> > > > +	struct ice_dpll_pin *p;
> > > > +	int ret = -EINVAL;
> > > > +
> > > > +	if (!pf)
> > > > +		return ret;
> > > > +	if (ice_dpll_cb_lock(pf))
> > > > +		return -EBUSY;
> > > > +	p = ice_find_pin(pf, pin, pin_type);
> > > 
> > > This does not make any sense to me. You should avoid the lookups and remove
> > > ice_find_pin() function entirely. The purpose of having pin_priv is to
> > > carry the struct ice_dpll_pin * directly. You should pass it down during
> > > pin register.
> > > 
> > > pf pointer is stored in dpll_priv.
> > > 
> > 
> > In this case dpll_priv is not passed, so cannot use it.
> 
> It should be passed. In general to every op where *dpll is passed, the
> dpll_priv pointer should be passed along. Please, fix this.
> 
> 
> > But in general it makes sense I will hold pf inside of ice_dpll_pin
> > and fix this.
> 
> Nope, just use dpll_priv. That's why we have it.
> 
> 
> [...]
> 
> 
> > > > +/**
> > > > + * ice_dpll_pin_state_set - set pin's state on dpll
> > > > + * @dpll: dpll being configured
> > > > + * @pin: pointer to a pin
> > > > + * @pin_priv: private data pointer passed on pin registration
> > > > + * @state: state of pin to be set
> > > > + * @extack: error reporting
> > > > + * @pin_type: type of a pin
> > > > + *
> > > > + * Set pin state on a pin.
> > > > + *
> > > > + * Return:
> > > > + * * 0 - OK or no change required
> > > > + * * negative - error
> > > > + */
> > > > +static int
> > > > +ice_dpll_pin_state_set(const struct dpll_device *dpll,
> > > > +		       const struct dpll_pin *pin, void *pin_priv,
> > > > +		       const enum dpll_pin_state state,
> > > 
> > > Why you use const with enums?
> > > 
> > 
> > Just show usage intention explicitly.
> 
> Does not make any sense what so ever. Please avoid it.
> 
> 
> > > > +static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
> > > > +					  void *pin_priv,
> > > > +					  const struct dpll_pin *parent_pin,
> > > > +					  enum dpll_pin_state *state,
> > > > +					  struct netlink_ext_ack *extack) {
> > > > +	struct ice_pf *pf = pin_priv;
> > > > +	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
> > > 
> > > Reverse christmas tree ordering please.
> > 
> > Fixed.
> > 
> > > 
> > > 
> > > > +	struct ice_dpll_pin *p;
> > > > +	int ret = -EFAULT;
> > > > +
> > > > +	if (!pf)
> > > 
> > > How exacly this can happen. My wild guess is it can't. Don't do such
> > > pointless checks please, confuses the reader.
> > > 
> > 
> > From driver perspective the pf pointer value is given by external entity,
> > why shouldn't it be valdiated?
> 
> What? You pass it during register, you get it back here. Nothing to
> check. Please drop it. Non-sense checks like this have no place in
> kernel, they only confuse reader as he/she assumes it is a valid case.
> 
> 
> [...]
> 
> 
> > > 
> > > 
> > > > +			pins[i].pin = NULL;
> > > > +			return -ENOMEM;
> > > > +		}
> > > > +		if (cgu) {
> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
> > > > +						pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
> > > > +						pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > 
> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
> > > here.
> > > 
> > 
> > No, in case of error, the caller releases everything ice_dpll_release_all(..).
> 
> 
> How does ice_dpll_release_all() where you failed? If you need to
> unregister one or both or none? I know that in ice you have odd ways to
> handle error paths in general, but this one clearly seems to be broken.
> 
> 
> 
> 
> 
> > 
> > > 
> > > > +		}
> > > > +	}
> > > > +	if (cgu) {
> > > > +		ops = &ice_dpll_output_ops;
> > > > +		pins = pf->dplls.outputs;
> > > > +		for (i = 0; i < pf->dplls.num_outputs; i++) {
> > > > +			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
> > > > +						   i + pf->dplls.num_inputs,
> > > > +						   THIS_MODULE, &pins[i].prop);
> > > > +			if (IS_ERR_OR_NULL(pins[i].pin)) {
> > > > +				pins[i].pin = NULL;
> > > > +				return -ENOMEM;
> > > 
> > > Don't make up error values when you get them from the function you call:
> > > 	return PTR_ERR(pins[i].pin);
> > 
> > Fixed.
> > 
> > > 
> > > > +			}
> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll, pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll, pins[i].pin,
> > > > +						ops, pf, NULL);
> > > > +			if (ret)
> > > > +				return ret;
> > > 
> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
> > > here.
> > > 
> > 
> > As above, in case of error, the caller releases everything.
> 
> As above, I don't think it works.
> 
> 
> [...]
> 
> 
> > > > +	}
> > > > +
> > > > +	if (cgu) {
> > > > +		ret = dpll_device_register(pf->dplls.eec.dpll, DPLL_TYPE_EEC,
> > > > +					   &ice_dpll_ops, pf, dev);
> > > > +		if (ret)
> > > > +			goto put_pps;
> > > > +		ret = dpll_device_register(pf->dplls.pps.dpll, DPLL_TYPE_PPS,
> > > > +					   &ice_dpll_ops, pf, dev);
> > > > +		if (ret)
> > > 
> > > You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
> > > DPLL_TYPE_EEC here. Fix the error path.
> > > 
> > 
> > The caller shall do the clean up, but yeah will fix this as here clean up
> > is not expected.
> 
> :) Just make your error paths obvious and easy to follow to not to
> confuse anybody, you included.

I agree with Jiri. The error paths here and in ice_dpll_init_info() are
quite confusing and IMHO error prone.

It will get more easy toread and more consistent if every
initialization function does return an error code would leave the state
clean in case of error. That is, in case of error, such function should
cleanup all the partially allocated/initialized resources.

Note that in ice_dpll_init_info() the situation is more mixed-up as
ice_dpll_release_info() is called on most error paths, except the last
one. Memory should not leaked due to later ice_dpll_release_all(), but
it's really confusing.

Cheers,

Paolo


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-04-28  0:20   ` Vadim Fedorenko
@ 2023-05-22 14:45     ` Paolo Abeni
  -1 siblings, 0 replies; 149+ messages in thread
From: Paolo Abeni @ 2023-05-22 14:45 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko,
	Arkadiusz Kubalewski, Jonathan Lemon
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 2023-04-27 at 17:20 -0700, Vadim Fedorenko wrote:
> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> 
> 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.
> 
> Co-developed-by: Milena Olech <milena.olech@intel.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Co-developed-by: Michal Michalik <michal.michalik@intel.com>
> Signed-off-by: Michal Michalik <michal.michalik@intel.com>
> Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

As this patch is quite big and tend to accumulate quite a few comments
- both hard to track and to address - I'm wondering if splitting it in
a few separated patches would could help?

e.g. 

- 1 patch for dpll device struct && APIs definition
- 1 patch for pin related APIs
- 1 patch for netlink notification.

(to be considered only if the effort for the above split is not
overwhelming)

Possibly the same could apply to patch 5/8.

Cheers,

Paolo


^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-05-22 14:45     ` Paolo Abeni
  0 siblings, 0 replies; 149+ messages in thread
From: Paolo Abeni @ 2023-05-22 14:45 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko,
	Arkadiusz Kubalewski, Jonathan Lemon
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-clk

On Thu, 2023-04-27 at 17:20 -0700, Vadim Fedorenko wrote:
> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> 
> 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.
> 
> Co-developed-by: Milena Olech <milena.olech@intel.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Co-developed-by: Michal Michalik <michal.michalik@intel.com>
> Signed-off-by: Michal Michalik <michal.michalik@intel.com>
> Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

As this patch is quite big and tend to accumulate quite a few comments
- both hard to track and to address - I'm wondering if splitting it in
a few separated patches would could help?

e.g. 

- 1 patch for dpll device struct && APIs definition
- 1 patch for pin related APIs
- 1 patch for netlink notification.

(to be considered only if the effort for the above split is not
overwhelming)

Possibly the same could apply to patch 5/8.

Cheers,

Paolo


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-19  6:15             ` Jiri Pirko
@ 2023-05-25  9:01               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25  9:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, May 19, 2023 8:15 AM
>
>Thu, May 18, 2023 at 06:06:03PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, May 16, 2023 8:26 AM
>>>
>>>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>>>
>>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>>>+			pins[i].pin = NULL;
>>>>>>+			return -ENOMEM;
>>>>>>+		}
>>>>>>+		if (cgu) {
>>>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>>>+						pins[i].pin,
>>>>>>+						ops, pf, NULL);
>>>>>>+			if (ret)
>>>>>>+				return ret;
>>>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>>>+						pins[i].pin,
>>>>>>+						ops, pf, NULL);
>>>>>>+			if (ret)
>>>>>>+				return ret;
>>>>>
>>>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin,
>>>>>..)
>>>>>here.
>>>>>
>>>>
>>>>No, in case of error, the caller releases everything
>>>ice_dpll_release_all(..).
>>>
>>>
>>>How does ice_dpll_release_all() where you failed? If you need to
>>>unregister one or both or none? I know that in ice you have odd ways to
>>>handle error paths in general, but this one clearly seems to be broken.
>>>
>>
>>It doesn't have to, as release all would release all anyway.
>>Leaving it for now.
>
>So you call dpll_pin_unregister() even for the pin that was not
>registered before? How is that even remotely correct?
>
>Fix your error paths, please. I don't understand the resistance here :)
>
>[...]

Fixed.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-25  9:01               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25  9:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, May 19, 2023 8:15 AM
>
>Thu, May 18, 2023 at 06:06:03PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, May 16, 2023 8:26 AM
>>>
>>>Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Wednesday, May 3, 2023 2:19 PM
>>>>>
>>>>>Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>>>+			pins[i].pin = NULL;
>>>>>>+			return -ENOMEM;
>>>>>>+		}
>>>>>>+		if (cgu) {
>>>>>>+			ret = dpll_pin_register(pf->dplls.eec.dpll,
>>>>>>+						pins[i].pin,
>>>>>>+						ops, pf, NULL);
>>>>>>+			if (ret)
>>>>>>+				return ret;
>>>>>>+			ret = dpll_pin_register(pf->dplls.pps.dpll,
>>>>>>+						pins[i].pin,
>>>>>>+						ops, pf, NULL);
>>>>>>+			if (ret)
>>>>>>+				return ret;
>>>>>
>>>>>You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin,
>>>>>..)
>>>>>here.
>>>>>
>>>>
>>>>No, in case of error, the caller releases everything
>>>ice_dpll_release_all(..).
>>>
>>>
>>>How does ice_dpll_release_all() where you failed? If you need to
>>>unregister one or both or none? I know that in ice you have odd ways to
>>>handle error paths in general, but this one clearly seems to be broken.
>>>
>>
>>It doesn't have to, as release all would release all anyway.
>>Leaving it for now.
>
>So you call dpll_pin_unregister() even for the pin that was not
>registered before? How is that even remotely correct?
>
>Fix your error paths, please. I don't understand the resistance here :)
>
>[...]

Fixed.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
  2023-05-19  6:47           ` Paolo Abeni
@ 2023-05-25  9:05             ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25  9:05 UTC (permalink / raw)
  To: Paolo Abeni, Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk


>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Friday, May 19, 2023 8:48 AM
>
>On Tue, 2023-05-16 at 08:26 +0200, Jiri Pirko wrote:
>> Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com
>> > > wrote:
>> > > From: Jiri Pirko <jiri@resnulli.us>
>> > > Sent: Wednesday, May 3, 2023 2:19 PM
>> > >
>> > > Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>> > > > From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>> [...]
>>
>>
>> > > > + * 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
>> > > > + * @frequency: frequency to be set
>> > > > + * @extack: error reporting
>> > > > + * @pin_type: type of pin being configured
>> > > > + *
>> > > > + * Wraps internal set frequency command on a pin.
>> > > > + *
>> > > > + * 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,
>> > > > +		       const u32 frequency,
>> > > > +		       struct netlink_ext_ack *extack,
>> > > > +		       const enum ice_dpll_pin_type pin_type) {
>> > > > +	struct ice_pf *pf = pin_priv;
>> > > > +	struct ice_dpll_pin *p;
>> > > > +	int ret = -EINVAL;
>> > > > +
>> > > > +	if (!pf)
>> > > > +		return ret;
>> > > > +	if (ice_dpll_cb_lock(pf))
>> > > > +		return -EBUSY;
>> > > > +	p = ice_find_pin(pf, pin, pin_type);
>> > >
>> > > This does not make any sense to me. You should avoid the lookups and
>> > > remove
>> > > ice_find_pin() function entirely. The purpose of having pin_priv is
>> > > to
>> > > carry the struct ice_dpll_pin * directly. You should pass it down
>> > > during
>> > > pin register.
>> > >
>> > > pf pointer is stored in dpll_priv.
>> > >
>> >
>> > In this case dpll_priv is not passed, so cannot use it.
>>
>> It should be passed. In general to every op where *dpll is passed, the
>> dpll_priv pointer should be passed along. Please, fix this.
>>
>>
>> > But in general it makes sense I will hold pf inside of ice_dpll_pin
>> > and fix this.
>>
>> Nope, just use dpll_priv. That's why we have it.
>>
>>
>> [...]
>>
>>
>> > > > +/**
>> > > > + * ice_dpll_pin_state_set - set pin's state on dpll
>> > > > + * @dpll: dpll being configured
>> > > > + * @pin: pointer to a pin
>> > > > + * @pin_priv: private data pointer passed on pin registration
>> > > > + * @state: state of pin to be set
>> > > > + * @extack: error reporting
>> > > > + * @pin_type: type of a pin
>> > > > + *
>> > > > + * Set pin state on a pin.
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0 - OK or no change required
>> > > > + * * negative - error
>> > > > + */
>> > > > +static int
>> > > > +ice_dpll_pin_state_set(const struct dpll_device *dpll,
>> > > > +		       const struct dpll_pin *pin, void *pin_priv,
>> > > > +		       const enum dpll_pin_state state,
>> > >
>> > > Why you use const with enums?
>> > >
>> >
>> > Just show usage intention explicitly.
>>
>> Does not make any sense what so ever. Please avoid it.
>>
>>
>> > > > +static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>> > > > +					  void *pin_priv,
>> > > > +					  const struct dpll_pin *parent_pin,
>> > > > +					  enum dpll_pin_state *state,
>> > > > +					  struct netlink_ext_ack *extack) {
>> > > > +	struct ice_pf *pf = pin_priv;
>> > > > +	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>> > >
>> > > Reverse christmas tree ordering please.
>> >
>> > Fixed.
>> >
>> > >
>> > >
>> > > > +	struct ice_dpll_pin *p;
>> > > > +	int ret = -EFAULT;
>> > > > +
>> > > > +	if (!pf)
>> > >
>> > > How exacly this can happen. My wild guess is it can't. Don't do such
>> > > pointless checks please, confuses the reader.
>> > >
>> >
>> > From driver perspective the pf pointer value is given by external entity,
>> > why shouldn't it be valdiated?
>>
>> What? You pass it during register, you get it back here. Nothing to
>> check. Please drop it. Non-sense checks like this have no place in
>> kernel, they only confuse reader as he/she assumes it is a valid case.
>>
>>
>> [...]
>>
>>
>> > >
>> > >
>> > > > +			pins[i].pin = NULL;
>> > > > +			return -ENOMEM;
>> > > > +		}
>> > > > +		if (cgu) {
>> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
>> > > > +						pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
>> > > > +						pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > >
>> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>> > > here.
>> > >
>> >
>> > No, in case of error, the caller releases everything
>> > ice_dpll_release_all(..).
>>
>>
>> How does ice_dpll_release_all() where you failed? If you need to
>> unregister one or both or none? I know that in ice you have odd ways to
>> handle error paths in general, but this one clearly seems to be broken.
>>
>>
>>
>>
>>
>> >
>> > >
>> > > > +		}
>> > > > +	}
>> > > > +	if (cgu) {
>> > > > +		ops = &ice_dpll_output_ops;
>> > > > +		pins = pf->dplls.outputs;
>> > > > +		for (i = 0; i < pf->dplls.num_outputs; i++) {
>> > > > +			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>> > > > +						   i + pf->dplls.num_inputs,
>> > > > +						   THIS_MODULE, &pins[i].prop);
>> > > > +			if (IS_ERR_OR_NULL(pins[i].pin)) {
>> > > > +				pins[i].pin = NULL;
>> > > > +				return -ENOMEM;
>> > >
>> > > Don't make up error values when you get them from the function you call:
>> > > 	return PTR_ERR(pins[i].pin);
>> >
>> > Fixed.
>> >
>> > >
>> > > > +			}
>> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
>> > > > pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
>> > > > pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > >
>> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin,
>>..)
>> > > here.
>> > >
>> >
>> > As above, in case of error, the caller releases everything.
>>
>> As above, I don't think it works.
>>
>>
>> [...]
>>
>>
>> > > > +	}
>> > > > +
>> > > > +	if (cgu) {
>> > > > +		ret = dpll_device_register(pf->dplls.eec.dpll,
>> > > > DPLL_TYPE_EEC,
>> > > > +					   &ice_dpll_ops, pf, dev);
>> > > > +		if (ret)
>> > > > +			goto put_pps;
>> > > > +		ret = dpll_device_register(pf->dplls.pps.dpll,
>> > > > DPLL_TYPE_PPS,
>> > > > +					   &ice_dpll_ops, pf, dev);
>> > > > +		if (ret)
>> > >
>> > > You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>> > > DPLL_TYPE_EEC here. Fix the error path.
>> > >
>> >
>> > The caller shall do the clean up, but yeah will fix this as here clean up
>> > is not expected.
>>
>> :) Just make your error paths obvious and easy to follow to not to
>> confuse anybody, you included.
>
>I agree with Jiri. The error paths here and in ice_dpll_init_info() are
>quite confusing and IMHO error prone.
>
>It will get more easy toread and more consistent if every
>initialization function does return an error code would leave the state
>clean in case of error. That is, in case of error, such function should
>cleanup all the partially allocated/initialized resources.
>
>Note that in ice_dpll_init_info() the situation is more mixed-up as
>ice_dpll_release_info() is called on most error paths, except the last
>one. Memory should not leaked due to later ice_dpll_release_all(), but
>it's really confusing.
>
>Cheers,
>
>Paolo

Fixed.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu
@ 2023-05-25  9:05             ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25  9:05 UTC (permalink / raw)
  To: Paolo Abeni, Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk


>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Friday, May 19, 2023 8:48 AM
>
>On Tue, 2023-05-16 at 08:26 +0200, Jiri Pirko wrote:
>> Tue, May 16, 2023 at 12:07:57AM CEST, arkadiusz.kubalewski@intel.com
>> > > wrote:
>> > > From: Jiri Pirko <jiri@resnulli.us>
>> > > Sent: Wednesday, May 3, 2023 2:19 PM
>> > >
>> > > Fri, Apr 28, 2023 at 02:20:06AM CEST, vadfed@meta.com wrote:
>> > > > From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>> [...]
>>
>>
>> > > > + * 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
>> > > > + * @frequency: frequency to be set
>> > > > + * @extack: error reporting
>> > > > + * @pin_type: type of pin being configured
>> > > > + *
>> > > > + * Wraps internal set frequency command on a pin.
>> > > > + *
>> > > > + * 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,
>> > > > +		       const u32 frequency,
>> > > > +		       struct netlink_ext_ack *extack,
>> > > > +		       const enum ice_dpll_pin_type pin_type) {
>> > > > +	struct ice_pf *pf = pin_priv;
>> > > > +	struct ice_dpll_pin *p;
>> > > > +	int ret = -EINVAL;
>> > > > +
>> > > > +	if (!pf)
>> > > > +		return ret;
>> > > > +	if (ice_dpll_cb_lock(pf))
>> > > > +		return -EBUSY;
>> > > > +	p = ice_find_pin(pf, pin, pin_type);
>> > >
>> > > This does not make any sense to me. You should avoid the lookups and
>> > > remove
>> > > ice_find_pin() function entirely. The purpose of having pin_priv is
>> > > to
>> > > carry the struct ice_dpll_pin * directly. You should pass it down
>> > > during
>> > > pin register.
>> > >
>> > > pf pointer is stored in dpll_priv.
>> > >
>> >
>> > In this case dpll_priv is not passed, so cannot use it.
>>
>> It should be passed. In general to every op where *dpll is passed, the
>> dpll_priv pointer should be passed along. Please, fix this.
>>
>>
>> > But in general it makes sense I will hold pf inside of ice_dpll_pin
>> > and fix this.
>>
>> Nope, just use dpll_priv. That's why we have it.
>>
>>
>> [...]
>>
>>
>> > > > +/**
>> > > > + * ice_dpll_pin_state_set - set pin's state on dpll
>> > > > + * @dpll: dpll being configured
>> > > > + * @pin: pointer to a pin
>> > > > + * @pin_priv: private data pointer passed on pin registration
>> > > > + * @state: state of pin to be set
>> > > > + * @extack: error reporting
>> > > > + * @pin_type: type of a pin
>> > > > + *
>> > > > + * Set pin state on a pin.
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0 - OK or no change required
>> > > > + * * negative - error
>> > > > + */
>> > > > +static int
>> > > > +ice_dpll_pin_state_set(const struct dpll_device *dpll,
>> > > > +		       const struct dpll_pin *pin, void *pin_priv,
>> > > > +		       const enum dpll_pin_state state,
>> > >
>> > > Why you use const with enums?
>> > >
>> >
>> > Just show usage intention explicitly.
>>
>> Does not make any sense what so ever. Please avoid it.
>>
>>
>> > > > +static int ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin,
>> > > > +					  void *pin_priv,
>> > > > +					  const struct dpll_pin *parent_pin,
>> > > > +					  enum dpll_pin_state *state,
>> > > > +					  struct netlink_ext_ack *extack) {
>> > > > +	struct ice_pf *pf = pin_priv;
>> > > > +	u32 parent_idx, hw_idx = ICE_DPLL_PIN_IDX_INVALID, i;
>> > >
>> > > Reverse christmas tree ordering please.
>> >
>> > Fixed.
>> >
>> > >
>> > >
>> > > > +	struct ice_dpll_pin *p;
>> > > > +	int ret = -EFAULT;
>> > > > +
>> > > > +	if (!pf)
>> > >
>> > > How exacly this can happen. My wild guess is it can't. Don't do such
>> > > pointless checks please, confuses the reader.
>> > >
>> >
>> > From driver perspective the pf pointer value is given by external entity,
>> > why shouldn't it be valdiated?
>>
>> What? You pass it during register, you get it back here. Nothing to
>> check. Please drop it. Non-sense checks like this have no place in
>> kernel, they only confuse reader as he/she assumes it is a valid case.
>>
>>
>> [...]
>>
>>
>> > >
>> > >
>> > > > +			pins[i].pin = NULL;
>> > > > +			return -ENOMEM;
>> > > > +		}
>> > > > +		if (cgu) {
>> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
>> > > > +						pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
>> > > > +						pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > >
>> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin, ..)
>> > > here.
>> > >
>> >
>> > No, in case of error, the caller releases everything
>> > ice_dpll_release_all(..).
>>
>>
>> How does ice_dpll_release_all() where you failed? If you need to
>> unregister one or both or none? I know that in ice you have odd ways to
>> handle error paths in general, but this one clearly seems to be broken.
>>
>>
>>
>>
>>
>> >
>> > >
>> > > > +		}
>> > > > +	}
>> > > > +	if (cgu) {
>> > > > +		ops = &ice_dpll_output_ops;
>> > > > +		pins = pf->dplls.outputs;
>> > > > +		for (i = 0; i < pf->dplls.num_outputs; i++) {
>> > > > +			pins[i].pin = dpll_pin_get(pf->dplls.clock_id,
>> > > > +						   i + pf->dplls.num_inputs,
>> > > > +						   THIS_MODULE, &pins[i].prop);
>> > > > +			if (IS_ERR_OR_NULL(pins[i].pin)) {
>> > > > +				pins[i].pin = NULL;
>> > > > +				return -ENOMEM;
>> > >
>> > > Don't make up error values when you get them from the function you call:
>> > > 	return PTR_ERR(pins[i].pin);
>> >
>> > Fixed.
>> >
>> > >
>> > > > +			}
>> > > > +			ret = dpll_pin_register(pf->dplls.eec.dpll,
>> > > > pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > > > +			ret = dpll_pin_register(pf->dplls.pps.dpll,
>> > > > pins[i].pin,
>> > > > +						ops, pf, NULL);
>> > > > +			if (ret)
>> > > > +				return ret;
>> > >
>> > > You have to call dpll_pin_unregister(pf->dplls.eec.dpll, pins[i].pin,
>>..)
>> > > here.
>> > >
>> >
>> > As above, in case of error, the caller releases everything.
>>
>> As above, I don't think it works.
>>
>>
>> [...]
>>
>>
>> > > > +	}
>> > > > +
>> > > > +	if (cgu) {
>> > > > +		ret = dpll_device_register(pf->dplls.eec.dpll,
>> > > > DPLL_TYPE_EEC,
>> > > > +					   &ice_dpll_ops, pf, dev);
>> > > > +		if (ret)
>> > > > +			goto put_pps;
>> > > > +		ret = dpll_device_register(pf->dplls.pps.dpll,
>> > > > DPLL_TYPE_PPS,
>> > > > +					   &ice_dpll_ops, pf, dev);
>> > > > +		if (ret)
>> > >
>> > > You are missing call to dpll_device_unregister(pf->dplls.eec.dpll,
>> > > DPLL_TYPE_EEC here. Fix the error path.
>> > >
>> >
>> > The caller shall do the clean up, but yeah will fix this as here clean up
>> > is not expected.
>>
>> :) Just make your error paths obvious and easy to follow to not to
>> confuse anybody, you included.
>
>I agree with Jiri. The error paths here and in ice_dpll_init_info() are
>quite confusing and IMHO error prone.
>
>It will get more easy toread and more consistent if every
>initialization function does return an error code would leave the state
>clean in case of error. That is, in case of error, such function should
>cleanup all the partially allocated/initialized resources.
>
>Note that in ice_dpll_init_info() the situation is more mixed-up as
>ice_dpll_release_info() is called on most error paths, except the last
>one. Memory should not leaked due to later ice_dpll_release_all(), but
>it's really confusing.
>
>Cheers,
>
>Paolo

Fixed.

Thank you,
Arkadiusz
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-05-02 13:04   ` Jiri Pirko
@ 2023-05-25 12:52     ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25 12:52 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 2, 2023 3:04 PM
>
>Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>[...]
>
>>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
>
>Arkadiusz, could you please expose pin for netdev in ice as well?
>
>
>>  mlx5: Implement SyncE support using DPLL infrastructure
>
>[...]

Yes, will do.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-25 12:52     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25 12:52 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 2, 2023 3:04 PM
>
>Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>[...]
>
>>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
>
>Arkadiusz, could you please expose pin for netdev in ice as well?
>
>
>>  mlx5: Implement SyncE support using DPLL infrastructure
>
>[...]

Yes, will do.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-05-11  7:52 ` Jiri Pirko
@ 2023-05-25 13:01     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25 13:01 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, May 11, 2023 9:53 AM
>
>Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>>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.
>>
>>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
>
>Vadim, did you try ynl monitor? I think there might be something wrong
>with the yaml spec:
># ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --
>subscribe monitor --sleep 10
>Unexpected msg id done while checking for ntf nl_len = 92 (76) nl_flags =
>0x0 nl_type = 19
>

One of the commits I have prepared already replaces old notifications with the
ones suggested previously, where format of notification is the same as format
of get command.
It works there, altough not sure if it works only for me or for everyone,
I might have done some modifications to ynl-lib itself, need to double check
that.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-25 13:01     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-25 13:01 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, May 11, 2023 9:53 AM
>
>Fri, Apr 28, 2023 at 02:20:01AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>>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.
>>
>>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
>
>Vadim, did you try ynl monitor? I think there might be something wrong
>with the yaml spec:
># ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --
>subscribe monitor --sleep 10
>Unexpected msg id done while checking for ntf nl_len = 92 (76) nl_flags =
>0x0 nl_type = 19
>

One of the commits I have prepared already replaces old notifications with the
ones suggested previously, where format of notification is the same as format
of get command.
It works there, altough not sure if it works only for me or for everyone,
I might have done some modifications to ynl-lib itself, need to double check
that.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-05-17 10:18   ` Jiri Pirko
@ 2023-05-26 10:14     ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-26 10:14 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 17, 2023 12:19 PM
>
>Let me summarize the outcome of the discussion between me and Jakub
>regarding attributes, handles and ID lookups in the RFCv7 thread:
>
>--------------------------------------------------------------
>** Needed changes for RFCv8 **
>
>1) No scoped indexes.
>   The indexes passed from driver to dpll core during call of:
>        dpll_device_get() - device_idx
>        dpll_pin_get() - pin_idx
>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>   Therefore following attributes need to be removed:
>   DPLL_A_PIN_IDX
>   DPLL_A_PIN_PARENT_IDX
>

Seems doable.
So just to be clear, configuring a pin-pair (MUXed pins) will now be done
with DPLL_A_PIN_PARENT nested attribute.
I.e. configuring state of pin on parent:
DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_PIN_ID	(id of parent pin)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-pair attributes to be set)

Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
I think there is no need for separated one, documentation shall just reflect
that.
Also we have nested attribute DPLL_A_DEVICE which is used to show connections
between PIN and multiple dpll devices, to make it consistent I will rename
it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
pair similar to the above:
DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_DEVICE_PARENT	(nested)
		DPLL_A_ID	(id of parent dpll)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-dpll attributes to be set)

Does it make sense?


>2) For device, the handle will be DPLL_A_ID == dpll->id.
>   This will be the only handle for device for every
>   device related GET, SET command and every device related notification.
>   Note: this ID is not deterministing and may be different depending on
>   order of device probes etc.
>

Seems doable.

>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>   This will be the only handle for pin for every
>   pin related GET, SET command and every pin related notification.
>   Note: this ID is not deterministing and may be different depending on
>   order of device probes etc.
>

Seems doable.

>4) Remove attribute:
>   DPLL_A_PIN_LABEL
>   and replace it with:
>   DPLL_A_PIN_PANEL_LABEL (string)
>   DPLL_A_PIN_XXX (string)
>   where XXX is a label type, like for example:
>     DPLL_A_PIN_BOARD_LABEL
>     DPLL_A_PIN_BOARD_TRACE
>     DPLL_A_PIN_PACKAGE_PIN
>

Sorry, I don't get this idea, what are those types?
What are they for?

>5) Make sure you expose following attributes for every device and
>   pin GET/DUMP command reply message:
>   DPLL_A_MODULE_NAME
>   DPLL_A_CLOCK_ID
>

Seems doable.

>6) Remove attributes:
>   DPLL_A_DEV_NAME
>   DPLL_A_BUS_NAME
>   as they no longer have any value and do no make sense (even in RFCv7)
>

Seems doable.

>
>--------------------------------------------------------------
>** Lookup commands **
>
>Basically these would allow user to query DEVICE_ID and PIN_ID
>according to provided atributes (see examples below).
>
>These would be from my perspective optional for this patchsets.
>I believe we can do it as follow-up if needed. For example for mlx5
>I don't have usecase for it, since I can consistently get PIN_ID
>using RT netlink for given netdev. But I can imagine that for non-SyncE
>dpll driver this would make sense to have.
>
>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>                          specified by given attrs
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>      DPLL_A_TYPE
>   <- DPLL_A_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- DPLL_A_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>   <- -EINVAL (Extack: "multiple devices matched")
>
>   If user passes a subset of attrs which would not result in
>   a single match, kernel returns -EINVAL and proper extack message.
>

Seems ok.

>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>                              specified by given attrs
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>      DPLL_A_PIN_TYPE
>      DPLL_A_PIN_PANEL_LABEL
>   <- DPLL_A_PIN_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- -EINVAL (Extack: "multiple pins matched")
>
>   If user passes a subset of attrs which would not result in
>   a single match, kernel returns -EINVAL and proper extack message.


Seems ok.

Will try to implement those now.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-26 10:14     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-05-26 10:14 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 17, 2023 12:19 PM
>
>Let me summarize the outcome of the discussion between me and Jakub
>regarding attributes, handles and ID lookups in the RFCv7 thread:
>
>--------------------------------------------------------------
>** Needed changes for RFCv8 **
>
>1) No scoped indexes.
>   The indexes passed from driver to dpll core during call of:
>        dpll_device_get() - device_idx
>        dpll_pin_get() - pin_idx
>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>   Therefore following attributes need to be removed:
>   DPLL_A_PIN_IDX
>   DPLL_A_PIN_PARENT_IDX
>

Seems doable.
So just to be clear, configuring a pin-pair (MUXed pins) will now be done
with DPLL_A_PIN_PARENT nested attribute.
I.e. configuring state of pin on parent:
DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_PIN_ID	(id of parent pin)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-pair attributes to be set)

Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
I think there is no need for separated one, documentation shall just reflect
that.
Also we have nested attribute DPLL_A_DEVICE which is used to show connections
between PIN and multiple dpll devices, to make it consistent I will rename
it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
pair similar to the above:
DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_DEVICE_PARENT	(nested)
		DPLL_A_ID	(id of parent dpll)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-dpll attributes to be set)

Does it make sense?


>2) For device, the handle will be DPLL_A_ID == dpll->id.
>   This will be the only handle for device for every
>   device related GET, SET command and every device related notification.
>   Note: this ID is not deterministing and may be different depending on
>   order of device probes etc.
>

Seems doable.

>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>   This will be the only handle for pin for every
>   pin related GET, SET command and every pin related notification.
>   Note: this ID is not deterministing and may be different depending on
>   order of device probes etc.
>

Seems doable.

>4) Remove attribute:
>   DPLL_A_PIN_LABEL
>   and replace it with:
>   DPLL_A_PIN_PANEL_LABEL (string)
>   DPLL_A_PIN_XXX (string)
>   where XXX is a label type, like for example:
>     DPLL_A_PIN_BOARD_LABEL
>     DPLL_A_PIN_BOARD_TRACE
>     DPLL_A_PIN_PACKAGE_PIN
>

Sorry, I don't get this idea, what are those types?
What are they for?

>5) Make sure you expose following attributes for every device and
>   pin GET/DUMP command reply message:
>   DPLL_A_MODULE_NAME
>   DPLL_A_CLOCK_ID
>

Seems doable.

>6) Remove attributes:
>   DPLL_A_DEV_NAME
>   DPLL_A_BUS_NAME
>   as they no longer have any value and do no make sense (even in RFCv7)
>

Seems doable.

>
>--------------------------------------------------------------
>** Lookup commands **
>
>Basically these would allow user to query DEVICE_ID and PIN_ID
>according to provided atributes (see examples below).
>
>These would be from my perspective optional for this patchsets.
>I believe we can do it as follow-up if needed. For example for mlx5
>I don't have usecase for it, since I can consistently get PIN_ID
>using RT netlink for given netdev. But I can imagine that for non-SyncE
>dpll driver this would make sense to have.
>
>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>                          specified by given attrs
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>      DPLL_A_TYPE
>   <- DPLL_A_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- DPLL_A_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>   <- -EINVAL (Extack: "multiple devices matched")
>
>   If user passes a subset of attrs which would not result in
>   a single match, kernel returns -EINVAL and proper extack message.
>

Seems ok.

>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>                              specified by given attrs
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>      DPLL_A_PIN_TYPE
>      DPLL_A_PIN_PANEL_LABEL
>   <- DPLL_A_PIN_ID
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>   Example:
>   -> DPLL_A_MODULE_NAME
>      DPLL_A_CLOCK_ID
>   <- -EINVAL (Extack: "multiple pins matched")
>
>   If user passes a subset of attrs which would not result in
>   a single match, kernel returns -EINVAL and proper extack message.


Seems ok.

Will try to implement those now.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-05-26 10:14     ` Kubalewski, Arkadiusz
@ 2023-05-26 10:39       ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-26 10:39 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, May 26, 2023 at 12:14:00PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, May 17, 2023 12:19 PM
>>
>>Let me summarize the outcome of the discussion between me and Jakub
>>regarding attributes, handles and ID lookups in the RFCv7 thread:
>>
>>--------------------------------------------------------------
>>** Needed changes for RFCv8 **
>>
>>1) No scoped indexes.
>>   The indexes passed from driver to dpll core during call of:
>>        dpll_device_get() - device_idx
>>        dpll_pin_get() - pin_idx
>>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>>   Therefore following attributes need to be removed:
>>   DPLL_A_PIN_IDX
>>   DPLL_A_PIN_PARENT_IDX
>>
>
>Seems doable.
>So just to be clear, configuring a pin-pair (MUXed pins) will now be done
>with DPLL_A_PIN_PARENT nested attribute.
>I.e. configuring state of pin on parent:
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_PIN_ID	(id of parent pin)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-pair attributes to be set)
>
>Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
>I think there is no need for separated one, documentation shall just reflect
>that.
>Also we have nested attribute DPLL_A_DEVICE which is used to show connections
>between PIN and multiple dpll devices, to make it consistent I will rename
>it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
>pair similar to the above:
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_DEVICE_PARENT	(nested)

It is a parent of pin, not device. The name is confusing. But see below.


>		DPLL_A_ID	(id of parent dpll)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-dpll attributes to be set)
>
>Does it make sense?

Yeah, good idea. I like this. We will have consistent approach for
parent pin and device. To take it even further, we can have one nested
attr for parent and decide the parent type according to the id attr
given:

DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_PIN_ID	(id of parent pin)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-pair attributes to be set)

DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_ID	(id of parent dpll)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-dpll attributes to be set)


Same for PIN_GET

Makes sense?



>
>
>>2) For device, the handle will be DPLL_A_ID == dpll->id.
>>   This will be the only handle for device for every
>>   device related GET, SET command and every device related notification.
>>   Note: this ID is not deterministing and may be different depending on
>>   order of device probes etc.
>>
>
>Seems doable.
>
>>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>>   This will be the only handle for pin for every
>>   pin related GET, SET command and every pin related notification.
>>   Note: this ID is not deterministing and may be different depending on
>>   order of device probes etc.
>>
>
>Seems doable.
>
>>4) Remove attribute:
>>   DPLL_A_PIN_LABEL
>>   and replace it with:
>>   DPLL_A_PIN_PANEL_LABEL (string)
>>   DPLL_A_PIN_XXX (string)
>>   where XXX is a label type, like for example:
>>     DPLL_A_PIN_BOARD_LABEL
>>     DPLL_A_PIN_BOARD_TRACE
>>     DPLL_A_PIN_PACKAGE_PIN
>>
>
>Sorry, I don't get this idea, what are those types?
>What are they for?

The point is to make the driver developer to think before passing
randomly constructed label strings. For example, "board_label" would lead
the developer to check how the pin is labeled on the board. The
"panel_label" indicates this is label on a panel. Also, developer can
fill multiple labels for the same pin.



>
>>5) Make sure you expose following attributes for every device and
>>   pin GET/DUMP command reply message:
>>   DPLL_A_MODULE_NAME
>>   DPLL_A_CLOCK_ID
>>
>
>Seems doable.
>
>>6) Remove attributes:
>>   DPLL_A_DEV_NAME
>>   DPLL_A_BUS_NAME
>>   as they no longer have any value and do no make sense (even in RFCv7)
>>
>
>Seems doable.
>
>>
>>--------------------------------------------------------------
>>** Lookup commands **
>>
>>Basically these would allow user to query DEVICE_ID and PIN_ID
>>according to provided atributes (see examples below).
>>
>>These would be from my perspective optional for this patchsets.
>>I believe we can do it as follow-up if needed. For example for mlx5
>>I don't have usecase for it, since I can consistently get PIN_ID
>>using RT netlink for given netdev. But I can imagine that for non-SyncE
>>dpll driver this would make sense to have.
>>
>>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>>                          specified by given attrs
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>      DPLL_A_TYPE
>>   <- DPLL_A_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- DPLL_A_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>   <- -EINVAL (Extack: "multiple devices matched")
>>
>>   If user passes a subset of attrs which would not result in
>>   a single match, kernel returns -EINVAL and proper extack message.
>>
>
>Seems ok.
>
>>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>>                              specified by given attrs
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>      DPLL_A_PIN_TYPE
>>      DPLL_A_PIN_PANEL_LABEL
>>   <- DPLL_A_PIN_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- -EINVAL (Extack: "multiple pins matched")
>>
>>   If user passes a subset of attrs which would not result in
>>   a single match, kernel returns -EINVAL and proper extack message.
>
>
>Seems ok.
>
>Will try to implement those now.

Cool, thx!


>
>Thank you,
>Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-05-26 10:39       ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-05-26 10:39 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, May 26, 2023 at 12:14:00PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, May 17, 2023 12:19 PM
>>
>>Let me summarize the outcome of the discussion between me and Jakub
>>regarding attributes, handles and ID lookups in the RFCv7 thread:
>>
>>--------------------------------------------------------------
>>** Needed changes for RFCv8 **
>>
>>1) No scoped indexes.
>>   The indexes passed from driver to dpll core during call of:
>>        dpll_device_get() - device_idx
>>        dpll_pin_get() - pin_idx
>>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>>   Therefore following attributes need to be removed:
>>   DPLL_A_PIN_IDX
>>   DPLL_A_PIN_PARENT_IDX
>>
>
>Seems doable.
>So just to be clear, configuring a pin-pair (MUXed pins) will now be done
>with DPLL_A_PIN_PARENT nested attribute.
>I.e. configuring state of pin on parent:
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_PIN_ID	(id of parent pin)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-pair attributes to be set)
>
>Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
>I think there is no need for separated one, documentation shall just reflect
>that.
>Also we have nested attribute DPLL_A_DEVICE which is used to show connections
>between PIN and multiple dpll devices, to make it consistent I will rename
>it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
>pair similar to the above:
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_DEVICE_PARENT	(nested)

It is a parent of pin, not device. The name is confusing. But see below.


>		DPLL_A_ID	(id of parent dpll)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-dpll attributes to be set)
>
>Does it make sense?

Yeah, good idea. I like this. We will have consistent approach for
parent pin and device. To take it even further, we can have one nested
attr for parent and decide the parent type according to the id attr
given:

DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_PIN_ID	(id of parent pin)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-pair attributes to be set)

DPLL_CMD_PIN_SET
	DPLL_A_PIN_ID		(id of pin being configured)
	DPLL_A_PIN_PARENT	(nested)
		DPLL_A_ID	(id of parent dpll)
		DPLL_A_PIN_STATE(expected state)
		...		(other pin-dpll attributes to be set)


Same for PIN_GET

Makes sense?



>
>
>>2) For device, the handle will be DPLL_A_ID == dpll->id.
>>   This will be the only handle for device for every
>>   device related GET, SET command and every device related notification.
>>   Note: this ID is not deterministing and may be different depending on
>>   order of device probes etc.
>>
>
>Seems doable.
>
>>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>>   This will be the only handle for pin for every
>>   pin related GET, SET command and every pin related notification.
>>   Note: this ID is not deterministing and may be different depending on
>>   order of device probes etc.
>>
>
>Seems doable.
>
>>4) Remove attribute:
>>   DPLL_A_PIN_LABEL
>>   and replace it with:
>>   DPLL_A_PIN_PANEL_LABEL (string)
>>   DPLL_A_PIN_XXX (string)
>>   where XXX is a label type, like for example:
>>     DPLL_A_PIN_BOARD_LABEL
>>     DPLL_A_PIN_BOARD_TRACE
>>     DPLL_A_PIN_PACKAGE_PIN
>>
>
>Sorry, I don't get this idea, what are those types?
>What are they for?

The point is to make the driver developer to think before passing
randomly constructed label strings. For example, "board_label" would lead
the developer to check how the pin is labeled on the board. The
"panel_label" indicates this is label on a panel. Also, developer can
fill multiple labels for the same pin.



>
>>5) Make sure you expose following attributes for every device and
>>   pin GET/DUMP command reply message:
>>   DPLL_A_MODULE_NAME
>>   DPLL_A_CLOCK_ID
>>
>
>Seems doable.
>
>>6) Remove attributes:
>>   DPLL_A_DEV_NAME
>>   DPLL_A_BUS_NAME
>>   as they no longer have any value and do no make sense (even in RFCv7)
>>
>
>Seems doable.
>
>>
>>--------------------------------------------------------------
>>** Lookup commands **
>>
>>Basically these would allow user to query DEVICE_ID and PIN_ID
>>according to provided atributes (see examples below).
>>
>>These would be from my perspective optional for this patchsets.
>>I believe we can do it as follow-up if needed. For example for mlx5
>>I don't have usecase for it, since I can consistently get PIN_ID
>>using RT netlink for given netdev. But I can imagine that for non-SyncE
>>dpll driver this would make sense to have.
>>
>>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>>                          specified by given attrs
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>      DPLL_A_TYPE
>>   <- DPLL_A_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- DPLL_A_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>   <- -EINVAL (Extack: "multiple devices matched")
>>
>>   If user passes a subset of attrs which would not result in
>>   a single match, kernel returns -EINVAL and proper extack message.
>>
>
>Seems ok.
>
>>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>>                              specified by given attrs
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>      DPLL_A_PIN_TYPE
>>      DPLL_A_PIN_PANEL_LABEL
>>   <- DPLL_A_PIN_ID
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>>   Example:
>>   -> DPLL_A_MODULE_NAME
>>      DPLL_A_CLOCK_ID
>>   <- -EINVAL (Extack: "multiple pins matched")
>>
>>   If user passes a subset of attrs which would not result in
>>   a single match, kernel returns -EINVAL and proper extack message.
>
>
>Seems ok.
>
>Will try to implement those now.

Cool, thx!


>
>Thank you,
>Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
  2023-05-26 10:39       ` Jiri Pirko
@ 2023-06-05 10:07         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-05 10:07 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, May 26, 2023 12:39 PM
>
>Fri, May 26, 2023 at 12:14:00PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, May 17, 2023 12:19 PM
>>>
>>>Let me summarize the outcome of the discussion between me and Jakub
>>>regarding attributes, handles and ID lookups in the RFCv7 thread:
>>>
>>>--------------------------------------------------------------
>>>** Needed changes for RFCv8 **
>>>
>>>1) No scoped indexes.
>>>   The indexes passed from driver to dpll core during call of:
>>>        dpll_device_get() - device_idx
>>>        dpll_pin_get() - pin_idx
>>>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>>>   Therefore following attributes need to be removed:
>>>   DPLL_A_PIN_IDX
>>>   DPLL_A_PIN_PARENT_IDX
>>>
>>
>>Seems doable.
>>So just to be clear, configuring a pin-pair (MUXed pins) will now be done
>>with DPLL_A_PIN_PARENT nested attribute.
>>I.e. configuring state of pin on parent:
>>DPLL_CMD_PIN_SET
>>	DPLL_A_PIN_ID		(id of pin being configured)
>>	DPLL_A_PIN_PARENT	(nested)
>>		DPLL_A_PIN_ID	(id of parent pin)
>>		DPLL_A_PIN_STATE(expected state)
>>		...		(other pin-pair attributes to be set)
>>
>>Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
>>I think there is no need for separated one, documentation shall just
>>reflect that.
>>Also we have nested attribute DPLL_A_DEVICE which is used to show
>connections
>>between PIN and multiple dpll devices, to make it consistent I will rename
>>it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
>>pair similar to the above:
>>DPLL_CMD_PIN_SET
>>	DPLL_A_PIN_ID		(id of pin being configured)
>>	DPLL_A_DEVICE_PARENT	(nested)
>
>It is a parent of pin, not device. The name is confusing. But see below.
>
>
>>		DPLL_A_ID	(id of parent dpll)
>>		DPLL_A_PIN_STATE(expected state)
>>		...		(other pin-dpll attributes to be set)
>>
>>Does it make sense?
>
>Yeah, good idea. I like this. We will have consistent approach for
>parent pin and device. To take it even further, we can have one nested
>attr for parent and decide the parent type according to the id attr
>given:
>
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_PIN_ID	(id of parent pin)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-pair attributes to be set)
>
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_ID	(id of parent dpll)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-dpll attributes to be set)
>
>
>Same for PIN_GET
>
>Makes sense?
>

Sure, fixed.

>
>
>>
>>
>>>2) For device, the handle will be DPLL_A_ID == dpll->id.
>>>   This will be the only handle for device for every
>>>   device related GET, SET command and every device related notification.
>>>   Note: this ID is not deterministing and may be different depending on
>>>   order of device probes etc.
>>>
>>
>>Seems doable.
>>
>>>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>>>   This will be the only handle for pin for every
>>>   pin related GET, SET command and every pin related notification.
>>>   Note: this ID is not deterministing and may be different depending on
>>>   order of device probes etc.
>>>
>>
>>Seems doable.
>>
>>>4) Remove attribute:
>>>   DPLL_A_PIN_LABEL
>>>   and replace it with:
>>>   DPLL_A_PIN_PANEL_LABEL (string)
>>>   DPLL_A_PIN_XXX (string)
>>>   where XXX is a label type, like for example:
>>>     DPLL_A_PIN_BOARD_LABEL
>>>     DPLL_A_PIN_BOARD_TRACE
>>>     DPLL_A_PIN_PACKAGE_PIN
>>>
>>
>>Sorry, I don't get this idea, what are those types?
>>What are they for?
>
>The point is to make the driver developer to think before passing
>randomly constructed label strings. For example, "board_label" would lead
>the developer to check how the pin is labeled on the board. The
>"panel_label" indicates this is label on a panel. Also, developer can
>fill multiple labels for the same pin.
>

Ok, makes sense, added as suggested.

Thank you,
Arkadiusz

>
>
>>
>>>5) Make sure you expose following attributes for every device and
>>>   pin GET/DUMP command reply message:
>>>   DPLL_A_MODULE_NAME
>>>   DPLL_A_CLOCK_ID
>>>
>>
>>Seems doable.
>>
>>>6) Remove attributes:
>>>   DPLL_A_DEV_NAME
>>>   DPLL_A_BUS_NAME
>>>   as they no longer have any value and do no make sense (even in RFCv7)
>>>
>>
>>Seems doable.
>>
>>>
>>>--------------------------------------------------------------
>>>** Lookup commands **
>>>
>>>Basically these would allow user to query DEVICE_ID and PIN_ID
>>>according to provided atributes (see examples below).
>>>
>>>These would be from my perspective optional for this patchsets.
>>>I believe we can do it as follow-up if needed. For example for mlx5
>>>I don't have usecase for it, since I can consistently get PIN_ID
>>>using RT netlink for given netdev. But I can imagine that for non-SyncE
>>>dpll driver this would make sense to have.
>>>
>>>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>>>                          specified by given attrs
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>      DPLL_A_TYPE
>>>   <- DPLL_A_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- DPLL_A_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>   <- -EINVAL (Extack: "multiple devices matched")
>>>
>>>   If user passes a subset of attrs which would not result in
>>>   a single match, kernel returns -EINVAL and proper extack message.
>>>
>>
>>Seems ok.
>>
>>>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>>>                              specified by given attrs
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>      DPLL_A_PIN_TYPE
>>>      DPLL_A_PIN_PANEL_LABEL
>>>   <- DPLL_A_PIN_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- -EINVAL (Extack: "multiple pins matched")
>>>
>>>   If user passes a subset of attrs which would not result in
>>>   a single match, kernel returns -EINVAL and proper extack message.
>>
>>
>>Seems ok.
>>
>>Will try to implement those now.
>
>Cool, thx!
>
>
>>
>>Thank you,
>>Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 0/8] Create common DPLL configuration API
@ 2023-06-05 10:07         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-05 10:07 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, May 26, 2023 12:39 PM
>
>Fri, May 26, 2023 at 12:14:00PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, May 17, 2023 12:19 PM
>>>
>>>Let me summarize the outcome of the discussion between me and Jakub
>>>regarding attributes, handles and ID lookups in the RFCv7 thread:
>>>
>>>--------------------------------------------------------------
>>>** Needed changes for RFCv8 **
>>>
>>>1) No scoped indexes.
>>>   The indexes passed from driver to dpll core during call of:
>>>        dpll_device_get() - device_idx
>>>        dpll_pin_get() - pin_idx
>>>   should be for INTERNAL kernel use only and NOT EXPOSED over uapi.
>>>   Therefore following attributes need to be removed:
>>>   DPLL_A_PIN_IDX
>>>   DPLL_A_PIN_PARENT_IDX
>>>
>>
>>Seems doable.
>>So just to be clear, configuring a pin-pair (MUXed pins) will now be done
>>with DPLL_A_PIN_PARENT nested attribute.
>>I.e. configuring state of pin on parent:
>>DPLL_CMD_PIN_SET
>>	DPLL_A_PIN_ID		(id of pin being configured)
>>	DPLL_A_PIN_PARENT	(nested)
>>		DPLL_A_PIN_ID	(id of parent pin)
>>		DPLL_A_PIN_STATE(expected state)
>>		...		(other pin-pair attributes to be set)
>>
>>Is that ok, or we need separated attribute like DPLL_A_PIN_PARENT_ID??
>>I think there is no need for separated one, documentation shall just
>>reflect that.
>>Also we have nested attribute DPLL_A_DEVICE which is used to show
>connections
>>between PIN and multiple dpll devices, to make it consistent I will rename
>>it to `DPLL_A_DEVICE_PARENT` and make configuration set cmd for the pin-dpll
>>pair similar to the above:
>>DPLL_CMD_PIN_SET
>>	DPLL_A_PIN_ID		(id of pin being configured)
>>	DPLL_A_DEVICE_PARENT	(nested)
>
>It is a parent of pin, not device. The name is confusing. But see below.
>
>
>>		DPLL_A_ID	(id of parent dpll)
>>		DPLL_A_PIN_STATE(expected state)
>>		...		(other pin-dpll attributes to be set)
>>
>>Does it make sense?
>
>Yeah, good idea. I like this. We will have consistent approach for
>parent pin and device. To take it even further, we can have one nested
>attr for parent and decide the parent type according to the id attr
>given:
>
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_PIN_ID	(id of parent pin)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-pair attributes to be set)
>
>DPLL_CMD_PIN_SET
>	DPLL_A_PIN_ID		(id of pin being configured)
>	DPLL_A_PIN_PARENT	(nested)
>		DPLL_A_ID	(id of parent dpll)
>		DPLL_A_PIN_STATE(expected state)
>		...		(other pin-dpll attributes to be set)
>
>
>Same for PIN_GET
>
>Makes sense?
>

Sure, fixed.

>
>
>>
>>
>>>2) For device, the handle will be DPLL_A_ID == dpll->id.
>>>   This will be the only handle for device for every
>>>   device related GET, SET command and every device related notification.
>>>   Note: this ID is not deterministing and may be different depending on
>>>   order of device probes etc.
>>>
>>
>>Seems doable.
>>
>>>3) For pin, the handle will be DPLL_A_PIN_ID == pin->id
>>>   This will be the only handle for pin for every
>>>   pin related GET, SET command and every pin related notification.
>>>   Note: this ID is not deterministing and may be different depending on
>>>   order of device probes etc.
>>>
>>
>>Seems doable.
>>
>>>4) Remove attribute:
>>>   DPLL_A_PIN_LABEL
>>>   and replace it with:
>>>   DPLL_A_PIN_PANEL_LABEL (string)
>>>   DPLL_A_PIN_XXX (string)
>>>   where XXX is a label type, like for example:
>>>     DPLL_A_PIN_BOARD_LABEL
>>>     DPLL_A_PIN_BOARD_TRACE
>>>     DPLL_A_PIN_PACKAGE_PIN
>>>
>>
>>Sorry, I don't get this idea, what are those types?
>>What are they for?
>
>The point is to make the driver developer to think before passing
>randomly constructed label strings. For example, "board_label" would lead
>the developer to check how the pin is labeled on the board. The
>"panel_label" indicates this is label on a panel. Also, developer can
>fill multiple labels for the same pin.
>

Ok, makes sense, added as suggested.

Thank you,
Arkadiusz

>
>
>>
>>>5) Make sure you expose following attributes for every device and
>>>   pin GET/DUMP command reply message:
>>>   DPLL_A_MODULE_NAME
>>>   DPLL_A_CLOCK_ID
>>>
>>
>>Seems doable.
>>
>>>6) Remove attributes:
>>>   DPLL_A_DEV_NAME
>>>   DPLL_A_BUS_NAME
>>>   as they no longer have any value and do no make sense (even in RFCv7)
>>>
>>
>>Seems doable.
>>
>>>
>>>--------------------------------------------------------------
>>>** Lookup commands **
>>>
>>>Basically these would allow user to query DEVICE_ID and PIN_ID
>>>according to provided atributes (see examples below).
>>>
>>>These would be from my perspective optional for this patchsets.
>>>I believe we can do it as follow-up if needed. For example for mlx5
>>>I don't have usecase for it, since I can consistently get PIN_ID
>>>using RT netlink for given netdev. But I can imagine that for non-SyncE
>>>dpll driver this would make sense to have.
>>>
>>>1) Introduce CMD_GET_ID - query the kernel for a dpll device
>>>                          specified by given attrs
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>      DPLL_A_TYPE
>>>   <- DPLL_A_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- DPLL_A_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>   <- -EINVAL (Extack: "multiple devices matched")
>>>
>>>   If user passes a subset of attrs which would not result in
>>>   a single match, kernel returns -EINVAL and proper extack message.
>>>
>>
>>Seems ok.
>>
>>>2) Introduce CMD_GET_PIN_ID - query the kernel for a dpll pin
>>>                              specified by given attrs
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>      DPLL_A_PIN_TYPE
>>>      DPLL_A_PIN_PANEL_LABEL
>>>   <- DPLL_A_PIN_ID
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- DPLL_A_PIN_ID    (There was only one pin for given module/clock_id)
>>>   Example:
>>>   -> DPLL_A_MODULE_NAME
>>>      DPLL_A_CLOCK_ID
>>>   <- -EINVAL (Extack: "multiple pins matched")
>>>
>>>   If user passes a subset of attrs which would not result in
>>>   a single match, kernel returns -EINVAL and proper extack message.
>>
>>
>>Seems ok.
>>
>>Will try to implement those now.
>
>Cool, thx!
>
>
>>
>>Thank you,
>>Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-02 15:38     ` Jiri Pirko
@ 2023-06-06 18:47       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:47 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 2, 2023 5:38 PM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>>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.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>---
>> MAINTAINERS                 |   8 +
>> drivers/Kconfig             |   2 +
>> drivers/Makefile            |   1 +
>> drivers/dpll/Kconfig        |   7 +
>> drivers/dpll/Makefile       |  10 +
>> drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    | 113 +++++
>> drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |  27 +
>> include/linux/dpll.h        | 274 ++++++++++
>> include/uapi/linux/dpll.h   |   2 +
>> 11 files changed, 2355 insertions(+)
>> 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 include/linux/dpll.h
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index ebd26b3ca90e..710976c0737e 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6302,6 +6302,14 @@ F:
>	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/swit
>ch-drive
>> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>>+DPLL CLOCK SUBSYSTEM
>>+M:	Vadim Fedorenko <vadfed@fb.com>
>>+L:	netdev@vger.kernel.org
>>+S:	Maintained
>>+F:	drivers/dpll/*
>>+F:	include/net/dpll.h
>>+F:	include/uapi/linux/dpll.h
>>+
>> DRBD DRIVER
>> M:	Philipp Reisner <philipp.reisner@linbit.com>
>> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>>diff --git a/drivers/Kconfig b/drivers/Kconfig
>>index 968bd0a6fd78..453df9e1210d 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
>>
>> source "drivers/hte/Kconfig"
>>
>>+source "drivers/dpll/Kconfig"
>>+
>> endmenu
>>diff --git a/drivers/Makefile b/drivers/Makefile
>>index 20b118dca999..9ffb554507ef 100644
>>--- a/drivers/Makefile
>>+++ b/drivers/Makefile
>>@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
>> obj-$(CONFIG_PECI)		+= peci/
>> obj-$(CONFIG_HTE)		+= hte/
>> obj-$(CONFIG_DRM_ACCEL)		+= accel/
>>+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..803bb5db7793
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,10 @@
>>+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..8a2370740026
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,939 @@
>>+// 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_xa_lock);
>
>Why this is called "xa_lock"? It protects much more than that. Call it
>dpll_big_lock while you are at it.
>

Sure, renamed to 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
>>+ *
>>+ * 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_device_get_by_name - find dpll device by it's id
>
>"by name" instead of "by id" ?
>

Function was removed.

>
>>+ * @bus_name: bus name of searched dpll
>>+ * @dev_name: dev name of searched dpll
>>+ *
>>+ * Return:
>>+ * * dpll_device struct if found
>>+ * * NULL otherwise
>>+ */
>>+struct dpll_device *
>>+dpll_device_get_by_name(const char *bus_name, const char *device_name)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long i;
>>+
>>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>>+			ret = dpll;
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+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.
>>+ *
>>+ * 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
>>+ *
>>+ * Decrement refcount of existing pin reference on given xarray.
>>+ * If all registrations are lifted delete the reference and free its memory.
>>+ *
>>+ * 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.
>>+ *
>>+ * 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
>>+ *
>>+ * Decrement refcount of existing dpll reference on given xarray.
>>+ * If all references are dropped, delete the reference and free its memory.
>>+ */
>>+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_dplls: dpll_pin_ref xarray holding dplls
>>+ * @dpll: pointer to a dpll
>>+ *
>>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>>+ *
>>+ * 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;
>>+}
>>+
>>+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.
>>+ *
>>+ * Return:
>>+ * * dpll_device struct pointer if succeeded
>>+ * * ERR_PTR(X) - failed allocation
>>+ */
>>+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.
>>+ *
>>+ * Return:
>>+ * * valid dpll_device struct pointer if succeeded
>>+ * * ERR_PTR of an error
>>+ */
>>+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_xa_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_xa_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
>>+ *
>>+ * 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_xa_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_xa_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
>>+ * @owner: pointer to owner device
>>+ *
>>+ * Make dpll device available for user space.
>>+ *
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL on failure
>
>From what I see, this function returns "-EEXIST" as well. Btw, what
>benefit this "table" brings? Perhaps could be avoided in the whole code?
>

Fixed, tried to improve the doxygen all over this file.

>
>>+ */
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+			 const struct dpll_device_ops *ops, void *priv,
>>+			 struct device *owner)
>>+{
>>+	struct dpll_device_registration *reg;
>>+	bool first_registration = false;
>>+
>>+	if (WARN_ON(!ops || !owner))
>>+		return -EINVAL;
>>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>>+	reg = dpll_device_registration_find(dpll, ops, priv);
>>+	if (reg) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return -EEXIST;
>>+	}
>>+
>>+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>+	if (!reg) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return -EEXIST;
>>+	}
>>+	reg->ops = ops;
>>+	reg->priv = priv;
>>+
>>+	dpll->dev.bus = owner->bus;
>
>This is definitelly odd. You basicall take PCI bus for example and
>pretend some other device to be there. Why exactly this dev is needed at
>all? I don't see the need, you only abuse it to store strings you
>expose over Netlink.
>
>Please remove dpll->dev entirely. Expose module_name, clock_id and
>device_idx directly over Netlink as separate attributes.
>
>
>>+	dpll->parent = owner;
>
>
>You don't use dpll->parent. Please remove and remove also "owner" arg of
>this function.
>

Removed.

>
>
>>+	dpll->type = type;
>>+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
>>+		     dpll->clock_id, dpll->device_idx);
>>+
>>+	first_registration = list_empty(&dpll->registration_list);
>>+	list_add_tail(&reg->list, &dpll->registration_list);
>>+	if (!first_registration) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return 0;
>>+	}
>>+
>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+	mutex_unlock(&dpll_xa_lock);
>>+	dpll_notify_device_create(dpll);
>>+
>>+	return 0;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_register);
>>+
>>+/**
>>+ * dpll_device_unregister - deregister dpll device
>>+ * @dpll: registered dpll pointer
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Deregister device, make it unavailable for userspace.
>>+ * Note: It does not free the memory
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+			    const struct dpll_device_ops *ops, void *priv)
>>+{
>>+	struct dpll_device_registration *reg;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>>+	ASSERT_DPLL_REGISTERED(dpll);
>>+
>>+	reg = dpll_device_registration_find(dpll, ops, priv);
>>+	if (WARN_ON(!reg)) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return;
>>+	}
>>+	list_del(&reg->list);
>>+	kfree(reg);
>>+
>>+	if (!list_empty(&dpll->registration_list)) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return;
>>+	}
>>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+	mutex_unlock(&dpll_xa_lock);
>>+	dpll_notify_device_delete(dpll);
>>+}
>>+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
>>+ *
>>+ * Return:
>>+ * valid allocated dpll_pin struct pointer if succeeded
>>+ * ERR_PTR of an error
>>+ */
>>+static struct dpll_pin *
>>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>
>DPLL_A_PIN_IDX is u32, in struct dpll_pin it is u32.
>Why here you have only u8? Please sync.
>

Fixed.

>
>>+	       const struct dpll_pin_properties *prop)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret, fs_size;
>>+
>>+	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;
>>+	refcount_set(&pin->refcount, 1);
>>+	if (WARN_ON(!prop->label)) {
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>>+	if (!pin->prop.label) {
>>+		ret = -ENOMEM;
>>+		goto err;
>>+	}
>>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.type = prop->type;
>>+	pin->prop.capabilities = prop->capabilities;
>
>Just assing the prop pointer to pin->prop and you are done, no. Why you
>need to copy the internals? Driver should behave and pass static const
>pointer here (it is common in cases like this).
>

Fixed.

>
>>+	if (prop->freq_supported_num) {
>>+		fs_size = sizeof(*pin->prop.freq_supported) *
>>+			  prop->freq_supported_num;
>>+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
>>+		if (!pin->prop.freq_supported) {
>>+			ret = -ENOMEM;
>>+			goto err;
>>+		}
>>+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
>>+		pin->prop.freq_supported_num = prop->freq_supported_num;
>>+	}
>>+	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->prop.label);
>>+	kfree(pin->rclk_dev_name);
>>+	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.
>>+ *
>>+ * 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
>>+ * @dpll: dpll_device struct pointer
>>+ *
>>+ * Drop reference for a pin, if all references are gone, delete pin object.
>>+ */
>>+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->prop.label);
>>+		kfree(pin->prop.freq_supported);
>>+		kfree(pin->rclk_dev_name);
>>+		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,
>>+		    const char *rclk_device_name)
>>+{
>>+	int ret;
>>+
>>+	if (WARN_ON(!ops))
>>+		return -EINVAL;
>>+
>>+	if (rclk_device_name && !pin->rclk_dev_name) {
>>+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
>>+		if (!pin->rclk_dev_name)
>>+			return -ENOMEM;
>>+	}
>>+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>>+	if (ret)
>>+		goto rclk_free;
>>+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>>+	if (ret)
>>+		goto ref_pin_del;
>>+	else
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
>>+
>>+	return ret;
>>+
>>+ref_pin_del:
>>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>>+rclk_free:
>>+	kfree(pin->rclk_dev_name);
>>+	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
>>+ * @rclk_device: pointer to recovered clock device
>>+ *
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL - missing dpll or pin
>
>Incorrect.
>

Fixed.

>
>>+ * * -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,
>>+		  struct device *rclk_device)
>>+{
>>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>+	int ret;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>
>You have to make sure that dpll and pin are created with same module and
>clock_id. Check and WARN_ON& bail out here.
>

Sure, makes sense, fixed.

>
>>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>>+	mutex_unlock(&dpll_xa_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 - deregister 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
>>+ */
>>+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_xa_lock);
>>+	__dpll_pin_unregister(dpll, pin, ops, priv);
>>+	mutex_unlock(&dpll_xa_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
>>+ * @rclk_device: pointer to recovered clock device
>>+ *
>>+ * Register a pin with a parent pin, create references between them and
>>+ * between newly registered pin and dplls connected with a parent pin.
>>+ *
>>+ * 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 device *rclk_device)
>>+{
>>+	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_xa_lock);
>>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>>+					  rclk_device ?
>>+					  dev_name(rclk_device) : NULL);
>>+		mutex_unlock(&dpll_xa_lock);
>>+		if (ret) {
>>+			stop = i;
>>+			goto dpll_unregister;
>>+		}
>>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>>+	}
>>+
>>+	return ret;
>>+
>>+dpll_unregister:
>>+	xa_for_each(&parent->dpll_refs, i, ref) {
>>+		if (i < stop) {
>>+			mutex_lock(&dpll_xa_lock);
>>+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
>>+			mutex_unlock(&dpll_xa_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 - deregister 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
>>+ *
>>+ * 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_xa_lock);
>>+	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);
>>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>>+				       DPLL_A_PIN_IDX);
>>+	}
>>+	mutex_unlock(&dpll_xa_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>>+
>>+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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_priv(const struct dpll_device *dpll)
>
>I don't see where you call this with const *. Avoid const here which
>will allow you to remove the cast below.
>

Fixed.

>
>>+{
>>+	struct dpll_device_registration *reg;
>>+
>>+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
>>+	return reg->priv;
>>+}
>>+
>>+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;
>>+}
>>+
>>+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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin_registration *reg;
>>+	struct dpll_pin_ref *ref;
>>+
>>+	ref = xa_load((struct xarray *)&dpll->pin_refs, pin->pin_idx);
>
>IDK, this sort of "unconst" cast always spells. Could you please
>avoid them in the entire code?
>

Fixed.

>
>>+	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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>>+			   const struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin_registration *reg;
>>+	struct dpll_pin_ref *ref;
>>+
>>+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
>>+	if (!ref)
>>+		return NULL;
>>+	reg = dpll_pin_registration_first(ref);
>>+	return reg->priv;
>>+}
>>+
>>+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;
>>+}
>>+
>>+static int __init dpll_init(void)
>>+{
>>+	int ret;
>>+
>>+	ret = dpll_netlink_init();
>>+	if (ret)
>>+		goto error;
>>+
>>+	return 0;
>>+
>>+error:
>>+	mutex_destroy(&dpll_xa_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..e905c1088568
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,113 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
>>+ * @id:			unique id number for each device
>>+ * @dev_driver_id:	id given by dev driver
>>+ * @clock_id:		unique identifier (clock_id) of a dpll
>>+ * @module:		module of creator
>>+ * @dev:		struct device for this dpll device
>>+ * @parent:		parent device
>>+ * @ops:		operations this &dpll_device supports
>>+ * @lock:		mutex to serialize operations
>>+ * @type:		type of a dpll
>>+ * @pins:		list of pointers to pins registered with this dpll
>>+ * @mode_supported_mask: mask of supported modes
>>+ * @refcount:		refcount
>>+ * @priv:		pointer to private information of owner
>>+ **/
>>+struct dpll_device {
>>+	u32 id;
>>+	u32 device_idx;
>>+	u64 clock_id;
>>+	struct module *module;
>>+	struct device dev;
>>+	struct device *parent;
>>+	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
>>+ * @idx:		unique idx given by alloc on global pin's XA
>>+ * @dev_driver_id:	id given by dev driver
>>+ * @clock_id:		clock_id of creator
>>+ * @module:		module of creator
>>+ * @dpll_refs:		hold referencees to dplls that pin is registered
>>with
>>+ * @pin_refs:		hold references to pins that pin is registered with
>>+ * @prop:		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;
>>+	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(const struct dpll_device *dpll);
>>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin);
>>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>>+			   const 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);
>>+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
>>+					    const char *dev_name);
>>+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_xa_lock;
>>+#endif
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..1eb0b4a2fce4
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,972 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * Generic netlink for DPLL management framework
>>+ *
>>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>
>It's 2023. You still live in the past :)
>
>

Fixed.

>
>>+ *
>>+ */
>>+#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>
>>+
>>+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;
>>+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))
>
>In this version, only ID is a handle.
>

Fixed.

>
>>+		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, const struct dpll_pin *pin,
>>+		      struct dpll_pin_ref *ref,
>>+		      struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const struct dpll_pin
>>*pin,
>>+			       struct dpll_pin_ref *ref,
>>+			       struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const struct dpll_pin
>>*pin,
>>+			   struct dpll_pin_ref *ref,
>>+			   struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const 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);
>>+	const 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 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);
>>+
>>+		ppin = ref->pin;
>>+
>>+		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, &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_PARENT_IDX, ppin->pin_idx)) {
>>+			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_DEVICE);
>>+		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;
>>+		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)
>>+{
>>+	int ret;
>>+
>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
>>+		return -EMSGSIZE;
>>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>>+		return -EMSGSIZE;
>>+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
>>+		return -EMSGSIZE;
>>+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	if (pin->rclk_dev_name)
>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>+				   pin->rclk_dev_name))
>>+			return -EMSGSIZE;
>>+	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, 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;
>>+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>>+			  &dpll->clock_id, 0))
>>+		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;
>>+
>>+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+					 dpll, dpll_priv(dpll), freq, extack);
>>+		if (ret)
>>+			return -EFAULT;
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			  u32 parent_idx, enum dpll_pin_state state,
>>+			  struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops;
>>+	struct dpll_pin_ref *pin_ref, *parent_ref;
>>+
>>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>>+		return -EOPNOTSUPP;
>>+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
>>+	       //	dpll_pin_get_by_idx(dpll, parent_idx);
>
>Leftover?

Fixed.

>
>
>
>>+	if (!parent_ref)
>>+		return -EINVAL;
>>+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>>+	if (!pin_ref)
>>+		return -EINVAL;
>>+	ops = dpll_pin_ops(pin_ref);
>>+	if (!ops->state_on_pin_set)
>>+		return -EOPNOTSUPP;
>>+	if (ops->state_on_pin_set(pin_ref->pin,
>>+				  dpll_pin_on_pin_priv(parent_ref->pin,
>>+						       pin_ref->pin),
>>+				  parent_ref->pin, state, extack))
>>+		return -EFAULT;
>>+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
>>+			       DPLL_A_PIN_STATE);
>>+
>>+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops;
>>+	struct dpll_pin_ref *ref;
>>+	u32 prio = nla_get_u8(prio_attr);
>>+
>>+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
>>+		       struct netlink_ext_ack *extack)
>>+{
>>+	enum dpll_pin_direction direction = nla_get_u8(a);
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long i;
>>+
>>+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
>>+		return -EOPNOTSUPP;
>>+
>>+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+				       dpll, dpll_priv(dpll), direction,
>>+				       extack))
>>+			return -EFAULT;
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct genl_info *info)
>>+{
>>+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
>>+	bool parent_present = false;
>>+	int rem, ret = -EINVAL;
>>+	struct nlattr *a;
>>+	u32 parent_idx;
>>+
>>+	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_DIRECTION:
>>+			ret = dpll_pin_direction_set(pin, a, info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLL_A_PIN_PRIO:
>>+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLL_A_PIN_PARENT_IDX:
>
>See my comment for dpll_pin_pre_doit(), please change this to
>PIN_PARENT_ID and use uniqueue xarray id handle for parent pin.
>

This has changed already, now parent comes as nested attribute
DPLL_A_PIN_PARENT and uses DPLL_A_ID or DPLL_A_PIN_ID, depending
if provides info on parent dpll or pin.

>
>>+			parent_present = true;
>>+			parent_idx = nla_get_u32(a);
>>+			break;
>>+		case DPLL_A_PIN_STATE:
>>+			state = nla_get_u8(a);
>>+			break;
>>+		default:
>>+			break;
>>+		}
>
>
>Why do you have to iterate here? Why simple
>	if (attr_x)
>		ret = dpll_pin_x_set()
>
>is not enough?
>
>Is it because of state? if yes:
>	if (attr_state)
>		if (attr_parent)
>			dpll_pin_on_pin_state_set()
>		else
>			dpll_pin_state_set()
>
>

Fixed.

>
>
>>+	}
>>+	if (state != DPLL_PIN_STATE_UNSPEC) {
>>+		if (!parent_present) {
>>+			ret = dpll_pin_state_set(dpll, pin, state,
>>+						 info->extack);
>>+			if (ret)
>>+				return ret;
>>+		} else {
>>+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
>>+							state, info->extack);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct dpll_pin *pin = info->user_ptr[1];
>>+
>>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>>+}
>>+
>>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_pin *pin = info->user_ptr[1];
>>+	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;
>>+}
>>+
>>+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 *attr;
>>+	enum dpll_mode mode;
>>+	int rem, ret = 0;
>>+
>>+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(attr)) {
>>+		case DPLL_A_MODE:
>
>Again, why loop? I don't see any sane reason, is there any?
>

No, no reason now, will remove it.

>
>
>>+			mode = nla_get_u8(attr);
>>+
>>+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
>>+					    info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+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_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_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_xa_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_xa_lock);
>>+	return -ENODEV;
>>+}
>>+
>>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+		    struct genl_info *info)
>>+{
>>+	mutex_unlock(&dpll_xa_lock);
>>+}
>>+
>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_lock(&dpll_xa_lock);
>>+
>>+	return 0;
>>+}
>>+
>>+int dpll_post_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_unlock(&dpll_xa_lock);
>>+
>>+	return 0;
>>+}
>>+
>>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+		      struct genl_info *info)
>>+{
>>+	int ret = dpll_pre_doit(ops, skb, info);
>>+	struct dpll_pin_ref *pin_ref;
>>+	struct dpll_device *dpll;
>>+
>>+	if (ret)
>>+		return ret;
>>+	dpll = info->user_ptr[0];
>>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>>+		ret = -EINVAL;
>>+		goto unlock_dev;
>>+	}
>>+	pin_ref = xa_load(&dpll->pin_refs,
>>+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>
>This is inconsistent, also incorrect.
>
>You use DPLL_A_ID that is stored in dpll_device_xa as a handle for device.
>That is fine if we consider Jakub's desire to have this randomly
>generated id as a handle (I find is questinable, but can live with it).
>
>But pins are independent on a single DPLL and could be attached to
>multiple ones. Using a single DPLL_A_ID as handle here (dpll_pre_doit)
>for all operations is plain wrong.
>
>For example for frequency or direction set, you don't need it in code as
>you iterate over all attacheds DPLL devices. Confusing to require DPLL
>device handle for that operation when you change setup for all of them.
>That is wrong.
>
>Also, you have global dpll_pin_xa. Yet you don't expose this ID over
>netlink. To be consistent with device handle, you should:
>1) expose pin->id over DPLL_A_PIN_ID
>2) use this DPLL_A_PIN_ID as a sole pin handle for dpll_pin_xa lookup.
>
>For DPLL-PIN tuple operations (prio_set and state_on_dpll_set)
>you should process the dpll device handle (DPLL_A_ID) where it is needed
>In the similar way you process parent id now where is it needed
>(state_on_pin_set)
>
>For GET/DUMP command, this does not also make sense.
>Check out __dpll_cmd_pin_dump_one()
>
>You just use the "first dpll" for the handle. Just use the pin->id as I
>suggested above.
>
>Makes sense?
>
>Please make sure you maintain the same handle attrs in the notification
>messages as well.
>
>

Yeah, with the changes to the other thread, this works now as you described.

>
>>+	if (!pin_ref) {
>>+		ret = -ENODEV;
>>+		goto unlock_dev;
>>+	}
>>+	info->user_ptr[1] = pin_ref->pin;
>>+
>>+	return 0;
>>+
>>+unlock_dev:
>>+	mutex_unlock(&dpll_xa_lock);
>>+	return ret;
>>+}
>>+
>>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff
>>*skb,
>>+			struct genl_info *info)
>>+{
>>+	dpll_post_doit(ops, skb, info);
>>+}
>>+
>>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	return dpll_pre_dumpit(cb);
>>+}
>>+
>>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>+{
>>+	return dpll_post_dumpit(cb);
>>+}
>>+
>>+static int
>>+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct dpll_pin *parent,
>>+			 enum dplla attr)
>>+{
>>+	int ret = dpll_msg_add_dev_handle(msg, dpll);
>>+	struct dpll_pin_ref *ref = NULL;
>
>Pointless init.
>

Fixed.

>
>>+	enum dpll_pin_state state;
>>+
>>+	if (ret)
>>+		return ret;
>>+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>>+		return -EMSGSIZE;
>>+
>>+	switch (attr) {
>>+	case DPLL_A_MODE:
>>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_LOCK_STATUS:
>>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_TEMP:
>>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_PIN_FREQUENCY:
>>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+		if (!ref)
>>+			return -EFAULT;
>>+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
>>+		break;
>>+	case DPLL_A_PIN_PRIO:
>>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+		if (!ref)
>>+			return -EFAULT;
>>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>
>Why exactly did you ignore my request I put in the previous version
>review asking to maintain the same nesting scheme for GET cmd and
>notification messages? Honestly, the silent ignores I'm getting
>all along the review of this patchset is very frustrating. Please don't
>do it. Either ack and change or provide exaplanation why your code is
>fine.
>
>So could you please fix this?
>Again, please make sure that the notification messages have attributes
>in exactly the same place as GET cmd (think of it as the rest of the
>attrs in the message is filtered out). Makes possible to use the same
>userspace parsing code both messages.
>
>

Fixed.

>
>>+		break;
>>+	case DPLL_A_PIN_STATE:
>>+		if (parent) {
>>+			const struct dpll_pin_ops *ops;
>>+			void *priv = dpll_pin_on_pin_priv(parent, pin);
>>+
>>+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
>>+			if (!ref)
>>+				return -EFAULT;
>>+			ops = dpll_pin_ops(ref);
>>+			if (!ops->state_on_pin_get)
>>+				return -EOPNOTSUPP;
>>+			ret = ops->state_on_pin_get(pin, priv, parent,
>>+						    &state, NULL);
>>+			if (ret)
>>+				return ret;
>>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>+					parent->pin_idx))
>>+				return -EMSGSIZE;
>>+		} else {
>>+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+			if (!ref)
>>+				return -EFAULT;
>>+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
>>+							     NULL);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	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_msg_add_dev_handle(msg, dpll);
>>+	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;
>>+}
>>+
>>+static int
>>+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		       struct dpll_pin *parent, enum dplla attr)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
>>+			  DPLL_EVENT_DEVICE_CHANGE);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
>>+	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_notify_device_create(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>
>Quite odd. Consider rename of dpll_send_event_create()
>

This has already changed.

>
>>+}
>>+
>>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
>>+{
>>+	if (WARN_ON(!dpll))
>>+		return -EINVAL;
>>+
>>+	return dpll_send_event_change(dpll, NULL, NULL, attr);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dplla attr)
>>+{
>>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>>+
>>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin
>>*pin,
>>+			   struct dpll_pin *parent, enum dplla attr)
>>+{
>>+	return dpll_send_event_change(dpll, pin, parent, attr);
>>+}
>>+
>>+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..952e0335595e
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.h
>>@@ -0,0 +1,27 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+/**
>>+ * dpll_notify_device_create - notify that the device has been created
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_create(struct dpll_device *dpll);
>>+
>>+
>>+/**
>>+ * dpll_notify_device_delete - notify that the device has been deleted
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_delete(struct dpll_device *dpll);
>>+
>>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			   struct dpll_pin *parent, enum dplla attr);
>>+
>>+int __init dpll_netlink_init(void);
>>+void dpll_netlink_finish(void);
>>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>>new file mode 100644
>>index 000000000000..5194efaf55a8
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,274 @@
>>+/* 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,
>>+				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,
>>+				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 *label;
>>+	enum dpll_pin_type type;
>>+	unsigned long capabilities;
>>+	u32 freq_supported_num;
>>+	struct dpll_pin_frequency *freq_supported;
>>+};
>>+
>>+/**
>>+ * dpll_device_get - find or create dpll_device object
>>+ * @clock_id: a system unique number for a device
>>+ * @dev_driver_id: index of dpll device on parent device
>>+ * @module: register module
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized dpll - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_device
>>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>>+
>>+/**
>>+ * dpll_device_put - caller drops reference to the device, free resources
>>+ * @dpll: dpll device pointer
>>+ *
>>+ * If all dpll_device_get callers drops their reference, the dpll device
>>+ * resources are freed.
>>+ */
>>+void dpll_device_put(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_device_register - register device, make it visible in the
>>subsystem.
>>+ * @dpll: reference previously allocated with dpll_device_get
>>+ * @type: type of dpll
>>+ * @ops: callbacks
>>+ * @priv: private data of registerer
>>+ * @owner: device struct of the owner
>>+ *
>>+ */
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+			 const struct dpll_device_ops *ops, void *priv,
>>+			 struct device *owner);
>>+
>>+/**
>>+ * dpll_device_unregister - deregister registered dpll
>>+ * @dpll: pointer to dpll
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>>+ * API users.
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+			    const struct dpll_device_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_get - get reference or create new pin object
>>+ * @clock_id: a system unique number of a device
>>+ * @@dev_driver_id: index of dpll device on parent device
>>+ * @module: register module
>>+ * @pin_prop: constant properities of a pin
>>+ *
>>+ * find existing pin with given clock_id, @dev_driver_id and module, or
>>create new
>>+ * and returen its reference.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized pin - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_pin
>>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>>+	      const struct dpll_pin_properties *prop);
>>+
>>+/**
>>+ * dpll_pin_register - register pin with a dpll device
>>+ * @dpll: pointer to dpll object to register pin with
>>+ * @pin: pointer to allocated pin object being registered with dpll
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * Register previously allocated pin object with a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this dpll,
>>+ * * -EBUSY - couldn't allocate id for a pin.
>>+ */
>
>I don't follow. In one of the previous version reviews I asked if you
>can have the function docs only in one place, preferably in .c. But you
>have there here in .h as well. Why? They are inconsistent. Could you
>please remove them from .h?
>

Fixed.

>
>
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      const struct dpll_pin_ops *ops, void *priv,
>>+		      struct device *rclk_device);
>
>As I asked in the previous version, could you please remove this
>rclk_device
>pointer. This is replaced by my patch allowing to expose dpll pin for
>netdev over RTnetlink.
>
>

Fixed.

>
>>+
>>+/**
>>+ * dpll_pin_unregister - deregister pin from a dpll device
>>+ * @dpll: pointer to dpll object to deregister pin from
>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>+ * @ops: ops for a dpll pin ops
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Deregister previously registered pin object from a dpll device.
>>+ *
>>+ */
>>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			 const struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
>>+ * @pin: pointer to allocated pin
>>+ *
>>+ * Pins shall be deregistered from all dpll devices before putting them,
>>+ * otherwise the memory won't be freed.
>>+ */
>>+void dpll_pin_put(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>>+ * @parent: parent pin pointer
>>+ * @pin: pointer to allocated pin object being registered with a parent pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ */
>>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>>+			     const struct dpll_pin_ops *ops, void *priv,
>>+			     struct device *rclk_device);
>>+
>>+/**
>>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>>+ * @parent: parent pin pointer
>>+ * @pin: pointer to allocated pin object being registered with a parent pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ */
>>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin
>>*pin,
>>+				const struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_device_notify - notify on dpll device change
>>+ * @dpll: dpll device pointer
>>+ * @attr: changed attribute
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dplla attr);
>>+
>>+
>>+
>>+#endif
>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>index e188bc189754..75eeaa4396eb 100644
>>--- a/include/uapi/linux/dpll.h
>>+++ b/include/uapi/linux/dpll.h
>>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
>>
>> #define DPLL_PIN_FREQUENCY_1_HZ		1
>> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
>>
>> /**
>>  * enum dpll_pin_state - defines possible states of a pin, valid values for
>>--
>>2.34.1
>>

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-06 18:47       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:47 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 2, 2023 5:38 PM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>>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.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>---
>> MAINTAINERS                 |   8 +
>> drivers/Kconfig             |   2 +
>> drivers/Makefile            |   1 +
>> drivers/dpll/Kconfig        |   7 +
>> drivers/dpll/Makefile       |  10 +
>> drivers/dpll/dpll_core.c    | 939 ++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    | 113 +++++
>> drivers/dpll/dpll_netlink.c | 972 ++++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |  27 +
>> include/linux/dpll.h        | 274 ++++++++++
>> include/uapi/linux/dpll.h   |   2 +
>> 11 files changed, 2355 insertions(+)
>> 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 include/linux/dpll.h
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index ebd26b3ca90e..710976c0737e 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6302,6 +6302,14 @@ F:
>	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/swit
>ch-drive
>> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>>+DPLL CLOCK SUBSYSTEM
>>+M:	Vadim Fedorenko <vadfed@fb.com>
>>+L:	netdev@vger.kernel.org
>>+S:	Maintained
>>+F:	drivers/dpll/*
>>+F:	include/net/dpll.h
>>+F:	include/uapi/linux/dpll.h
>>+
>> DRBD DRIVER
>> M:	Philipp Reisner <philipp.reisner@linbit.com>
>> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>>diff --git a/drivers/Kconfig b/drivers/Kconfig
>>index 968bd0a6fd78..453df9e1210d 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
>>
>> source "drivers/hte/Kconfig"
>>
>>+source "drivers/dpll/Kconfig"
>>+
>> endmenu
>>diff --git a/drivers/Makefile b/drivers/Makefile
>>index 20b118dca999..9ffb554507ef 100644
>>--- a/drivers/Makefile
>>+++ b/drivers/Makefile
>>@@ -194,3 +194,4 @@ obj-$(CONFIG_MOST)		+= most/
>> obj-$(CONFIG_PECI)		+= peci/
>> obj-$(CONFIG_HTE)		+= hte/
>> obj-$(CONFIG_DRM_ACCEL)		+= accel/
>>+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..803bb5db7793
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,10 @@
>>+# 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/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..8a2370740026
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,939 @@
>>+// 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_xa_lock);
>
>Why this is called "xa_lock"? It protects much more than that. Call it
>dpll_big_lock while you are at it.
>

Sure, renamed to 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
>>+ *
>>+ * 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_device_get_by_name - find dpll device by it's id
>
>"by name" instead of "by id" ?
>

Function was removed.

>
>>+ * @bus_name: bus name of searched dpll
>>+ * @dev_name: dev name of searched dpll
>>+ *
>>+ * Return:
>>+ * * dpll_device struct if found
>>+ * * NULL otherwise
>>+ */
>>+struct dpll_device *
>>+dpll_device_get_by_name(const char *bus_name, const char *device_name)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long i;
>>+
>>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>>+			ret = dpll;
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+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.
>>+ *
>>+ * 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
>>+ *
>>+ * Decrement refcount of existing pin reference on given xarray.
>>+ * If all registrations are lifted delete the reference and free its memory.
>>+ *
>>+ * 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.
>>+ *
>>+ * 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
>>+ *
>>+ * Decrement refcount of existing dpll reference on given xarray.
>>+ * If all references are dropped, delete the reference and free its memory.
>>+ */
>>+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_dplls: dpll_pin_ref xarray holding dplls
>>+ * @dpll: pointer to a dpll
>>+ *
>>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>>+ *
>>+ * 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;
>>+}
>>+
>>+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.
>>+ *
>>+ * Return:
>>+ * * dpll_device struct pointer if succeeded
>>+ * * ERR_PTR(X) - failed allocation
>>+ */
>>+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.
>>+ *
>>+ * Return:
>>+ * * valid dpll_device struct pointer if succeeded
>>+ * * ERR_PTR of an error
>>+ */
>>+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_xa_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_xa_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
>>+ *
>>+ * 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_xa_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_xa_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
>>+ * @owner: pointer to owner device
>>+ *
>>+ * Make dpll device available for user space.
>>+ *
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL on failure
>
>From what I see, this function returns "-EEXIST" as well. Btw, what
>benefit this "table" brings? Perhaps could be avoided in the whole code?
>

Fixed, tried to improve the doxygen all over this file.

>
>>+ */
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+			 const struct dpll_device_ops *ops, void *priv,
>>+			 struct device *owner)
>>+{
>>+	struct dpll_device_registration *reg;
>>+	bool first_registration = false;
>>+
>>+	if (WARN_ON(!ops || !owner))
>>+		return -EINVAL;
>>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>>+	reg = dpll_device_registration_find(dpll, ops, priv);
>>+	if (reg) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return -EEXIST;
>>+	}
>>+
>>+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>+	if (!reg) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return -EEXIST;
>>+	}
>>+	reg->ops = ops;
>>+	reg->priv = priv;
>>+
>>+	dpll->dev.bus = owner->bus;
>
>This is definitelly odd. You basicall take PCI bus for example and
>pretend some other device to be there. Why exactly this dev is needed at
>all? I don't see the need, you only abuse it to store strings you
>expose over Netlink.
>
>Please remove dpll->dev entirely. Expose module_name, clock_id and
>device_idx directly over Netlink as separate attributes.
>
>
>>+	dpll->parent = owner;
>
>
>You don't use dpll->parent. Please remove and remove also "owner" arg of
>this function.
>

Removed.

>
>
>>+	dpll->type = type;
>>+	dev_set_name(&dpll->dev, "%s/%llx/%d", module_name(dpll->module),
>>+		     dpll->clock_id, dpll->device_idx);
>>+
>>+	first_registration = list_empty(&dpll->registration_list);
>>+	list_add_tail(&reg->list, &dpll->registration_list);
>>+	if (!first_registration) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return 0;
>>+	}
>>+
>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+	mutex_unlock(&dpll_xa_lock);
>>+	dpll_notify_device_create(dpll);
>>+
>>+	return 0;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_register);
>>+
>>+/**
>>+ * dpll_device_unregister - deregister dpll device
>>+ * @dpll: registered dpll pointer
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Deregister device, make it unavailable for userspace.
>>+ * Note: It does not free the memory
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+			    const struct dpll_device_ops *ops, void *priv)
>>+{
>>+	struct dpll_device_registration *reg;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>>+	ASSERT_DPLL_REGISTERED(dpll);
>>+
>>+	reg = dpll_device_registration_find(dpll, ops, priv);
>>+	if (WARN_ON(!reg)) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return;
>>+	}
>>+	list_del(&reg->list);
>>+	kfree(reg);
>>+
>>+	if (!list_empty(&dpll->registration_list)) {
>>+		mutex_unlock(&dpll_xa_lock);
>>+		return;
>>+	}
>>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+	mutex_unlock(&dpll_xa_lock);
>>+	dpll_notify_device_delete(dpll);
>>+}
>>+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
>>+ *
>>+ * Return:
>>+ * valid allocated dpll_pin struct pointer if succeeded
>>+ * ERR_PTR of an error
>>+ */
>>+static struct dpll_pin *
>>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>
>DPLL_A_PIN_IDX is u32, in struct dpll_pin it is u32.
>Why here you have only u8? Please sync.
>

Fixed.

>
>>+	       const struct dpll_pin_properties *prop)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret, fs_size;
>>+
>>+	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;
>>+	refcount_set(&pin->refcount, 1);
>>+	if (WARN_ON(!prop->label)) {
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>>+	if (!pin->prop.label) {
>>+		ret = -ENOMEM;
>>+		goto err;
>>+	}
>>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.type = prop->type;
>>+	pin->prop.capabilities = prop->capabilities;
>
>Just assing the prop pointer to pin->prop and you are done, no. Why you
>need to copy the internals? Driver should behave and pass static const
>pointer here (it is common in cases like this).
>

Fixed.

>
>>+	if (prop->freq_supported_num) {
>>+		fs_size = sizeof(*pin->prop.freq_supported) *
>>+			  prop->freq_supported_num;
>>+		pin->prop.freq_supported = kzalloc(fs_size, GFP_KERNEL);
>>+		if (!pin->prop.freq_supported) {
>>+			ret = -ENOMEM;
>>+			goto err;
>>+		}
>>+		memcpy(pin->prop.freq_supported, prop->freq_supported, fs_size);
>>+		pin->prop.freq_supported_num = prop->freq_supported_num;
>>+	}
>>+	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->prop.label);
>>+	kfree(pin->rclk_dev_name);
>>+	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.
>>+ *
>>+ * 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
>>+ * @dpll: dpll_device struct pointer
>>+ *
>>+ * Drop reference for a pin, if all references are gone, delete pin object.
>>+ */
>>+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->prop.label);
>>+		kfree(pin->prop.freq_supported);
>>+		kfree(pin->rclk_dev_name);
>>+		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,
>>+		    const char *rclk_device_name)
>>+{
>>+	int ret;
>>+
>>+	if (WARN_ON(!ops))
>>+		return -EINVAL;
>>+
>>+	if (rclk_device_name && !pin->rclk_dev_name) {
>>+		pin->rclk_dev_name = kstrdup(rclk_device_name, GFP_KERNEL);
>>+		if (!pin->rclk_dev_name)
>>+			return -ENOMEM;
>>+	}
>>+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>>+	if (ret)
>>+		goto rclk_free;
>>+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>>+	if (ret)
>>+		goto ref_pin_del;
>>+	else
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_IDX);
>>+
>>+	return ret;
>>+
>>+ref_pin_del:
>>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>>+rclk_free:
>>+	kfree(pin->rclk_dev_name);
>>+	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
>>+ * @rclk_device: pointer to recovered clock device
>>+ *
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL - missing dpll or pin
>
>Incorrect.
>

Fixed.

>
>>+ * * -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,
>>+		  struct device *rclk_device)
>>+{
>>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>+	int ret;
>>+
>>+	mutex_lock(&dpll_xa_lock);
>
>You have to make sure that dpll and pin are created with same module and
>clock_id. Check and WARN_ON& bail out here.
>

Sure, makes sense, fixed.

>
>>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>>+	mutex_unlock(&dpll_xa_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 - deregister 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
>>+ */
>>+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_xa_lock);
>>+	__dpll_pin_unregister(dpll, pin, ops, priv);
>>+	mutex_unlock(&dpll_xa_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
>>+ * @rclk_device: pointer to recovered clock device
>>+ *
>>+ * Register a pin with a parent pin, create references between them and
>>+ * between newly registered pin and dplls connected with a parent pin.
>>+ *
>>+ * 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 device *rclk_device)
>>+{
>>+	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_xa_lock);
>>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>>+					  rclk_device ?
>>+					  dev_name(rclk_device) : NULL);
>>+		mutex_unlock(&dpll_xa_lock);
>>+		if (ret) {
>>+			stop = i;
>>+			goto dpll_unregister;
>>+		}
>>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>>+	}
>>+
>>+	return ret;
>>+
>>+dpll_unregister:
>>+	xa_for_each(&parent->dpll_refs, i, ref) {
>>+		if (i < stop) {
>>+			mutex_lock(&dpll_xa_lock);
>>+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
>>+			mutex_unlock(&dpll_xa_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 - deregister 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
>>+ *
>>+ * 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_xa_lock);
>>+	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);
>>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>>+				       DPLL_A_PIN_IDX);
>>+	}
>>+	mutex_unlock(&dpll_xa_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>>+
>>+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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_priv(const struct dpll_device *dpll)
>
>I don't see where you call this with const *. Avoid const here which
>will allow you to remove the cast below.
>

Fixed.

>
>>+{
>>+	struct dpll_device_registration *reg;
>>+
>>+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
>>+	return reg->priv;
>>+}
>>+
>>+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;
>>+}
>>+
>>+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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin_registration *reg;
>>+	struct dpll_pin_ref *ref;
>>+
>>+	ref = xa_load((struct xarray *)&dpll->pin_refs, pin->pin_idx);
>
>IDK, this sort of "unconst" cast always spells. Could you please
>avoid them in the entire code?
>

Fixed.

>
>>+	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
>>+ *
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>>+			   const struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin_registration *reg;
>>+	struct dpll_pin_ref *ref;
>>+
>>+	ref = xa_load((struct xarray *)&pin->parent_refs, parent->pin_idx);
>>+	if (!ref)
>>+		return NULL;
>>+	reg = dpll_pin_registration_first(ref);
>>+	return reg->priv;
>>+}
>>+
>>+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;
>>+}
>>+
>>+static int __init dpll_init(void)
>>+{
>>+	int ret;
>>+
>>+	ret = dpll_netlink_init();
>>+	if (ret)
>>+		goto error;
>>+
>>+	return 0;
>>+
>>+error:
>>+	mutex_destroy(&dpll_xa_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..e905c1088568
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,113 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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 - structure for a DPLL device
>>+ * @id:			unique id number for each device
>>+ * @dev_driver_id:	id given by dev driver
>>+ * @clock_id:		unique identifier (clock_id) of a dpll
>>+ * @module:		module of creator
>>+ * @dev:		struct device for this dpll device
>>+ * @parent:		parent device
>>+ * @ops:		operations this &dpll_device supports
>>+ * @lock:		mutex to serialize operations
>>+ * @type:		type of a dpll
>>+ * @pins:		list of pointers to pins registered with this dpll
>>+ * @mode_supported_mask: mask of supported modes
>>+ * @refcount:		refcount
>>+ * @priv:		pointer to private information of owner
>>+ **/
>>+struct dpll_device {
>>+	u32 id;
>>+	u32 device_idx;
>>+	u64 clock_id;
>>+	struct module *module;
>>+	struct device dev;
>>+	struct device *parent;
>>+	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
>>+ * @idx:		unique idx given by alloc on global pin's XA
>>+ * @dev_driver_id:	id given by dev driver
>>+ * @clock_id:		clock_id of creator
>>+ * @module:		module of creator
>>+ * @dpll_refs:		hold referencees to dplls that pin is registered
>>with
>>+ * @pin_refs:		hold references to pins that pin is registered with
>>+ * @prop:		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;
>>+	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(const struct dpll_device *dpll);
>>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin);
>>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent,
>>+			   const 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);
>>+struct dpll_device *dpll_device_get_by_name(const char *bus_name,
>>+					    const char *dev_name);
>>+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_xa_lock;
>>+#endif
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..1eb0b4a2fce4
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,972 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * Generic netlink for DPLL management framework
>>+ *
>>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>
>It's 2023. You still live in the past :)
>
>

Fixed.

>
>>+ *
>>+ */
>>+#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>
>>+
>>+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;
>>+	if (nla_put_string(msg, DPLL_A_BUS_NAME, dev_bus_name(&dpll->dev)))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_DEV_NAME, dev_name(&dpll->dev)))
>
>In this version, only ID is a handle.
>

Fixed.

>
>>+		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, const struct dpll_pin *pin,
>>+		      struct dpll_pin_ref *ref,
>>+		      struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const struct dpll_pin
>>*pin,
>>+			       struct dpll_pin_ref *ref,
>>+			       struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const struct dpll_pin
>>*pin,
>>+			   struct dpll_pin_ref *ref,
>>+			   struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+	const 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, const 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);
>>+	const 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 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);
>>+
>>+		ppin = ref->pin;
>>+
>>+		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, &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_PARENT_IDX, ppin->pin_idx)) {
>>+			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_DEVICE);
>>+		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;
>>+		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)
>>+{
>>+	int ret;
>>+
>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_PIN_LABEL, pin->prop.label))
>>+		return -EMSGSIZE;
>>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>>+		return -EMSGSIZE;
>>+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, pin->prop.capabilities))
>>+		return -EMSGSIZE;
>>+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	if (pin->rclk_dev_name)
>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>+				   pin->rclk_dev_name))
>>+			return -EMSGSIZE;
>>+	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, 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;
>>+	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_UNSPEC + 1; 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_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>>+			  &dpll->clock_id, 0))
>>+		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;
>>+
>>+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+					 dpll, dpll_priv(dpll), freq, extack);
>>+		if (ret)
>>+			return -EFAULT;
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_FREQUENCY);
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			  u32 parent_idx, enum dpll_pin_state state,
>>+			  struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops;
>>+	struct dpll_pin_ref *pin_ref, *parent_ref;
>>+
>>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>>+		return -EOPNOTSUPP;
>>+	parent_ref = xa_load(&pin->parent_refs, parent_idx);
>>+	       //	dpll_pin_get_by_idx(dpll, parent_idx);
>
>Leftover?

Fixed.

>
>
>
>>+	if (!parent_ref)
>>+		return -EINVAL;
>>+	pin_ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>>+	if (!pin_ref)
>>+		return -EINVAL;
>>+	ops = dpll_pin_ops(pin_ref);
>>+	if (!ops->state_on_pin_set)
>>+		return -EOPNOTSUPP;
>>+	if (ops->state_on_pin_set(pin_ref->pin,
>>+				  dpll_pin_on_pin_priv(parent_ref->pin,
>>+						       pin_ref->pin),
>>+				  parent_ref->pin, state, extack))
>>+		return -EFAULT;
>>+	dpll_pin_parent_notify(dpll, pin_ref->pin, parent_ref->pin,
>>+			       DPLL_A_PIN_STATE);
>>+
>>+	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_notify(dpll, pin, DPLL_A_PIN_STATE);
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		  struct nlattr *prio_attr, struct netlink_ext_ack *extack)
>>+{
>>+	const struct dpll_pin_ops *ops;
>>+	struct dpll_pin_ref *ref;
>>+	u32 prio = nla_get_u8(prio_attr);
>>+
>>+	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_notify(dpll, pin, DPLL_A_PIN_PRIO);
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
>>+		       struct netlink_ext_ack *extack)
>>+{
>>+	enum dpll_pin_direction direction = nla_get_u8(a);
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long i;
>>+
>>+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop.capabilities))
>>+		return -EOPNOTSUPP;
>>+
>>+	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->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+				       dpll, dpll_priv(dpll), direction,
>>+				       extack))
>>+			return -EFAULT;
>>+		dpll_pin_notify(dpll, pin, DPLL_A_PIN_DIRECTION);
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct genl_info *info)
>>+{
>>+	enum dpll_pin_state state = DPLL_PIN_STATE_UNSPEC;
>>+	bool parent_present = false;
>>+	int rem, ret = -EINVAL;
>>+	struct nlattr *a;
>>+	u32 parent_idx;
>>+
>>+	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_DIRECTION:
>>+			ret = dpll_pin_direction_set(pin, a, info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLL_A_PIN_PRIO:
>>+			ret = dpll_pin_prio_set(dpll, pin, a, info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLL_A_PIN_PARENT_IDX:
>
>See my comment for dpll_pin_pre_doit(), please change this to
>PIN_PARENT_ID and use uniqueue xarray id handle for parent pin.
>

This has changed already, now parent comes as nested attribute
DPLL_A_PIN_PARENT and uses DPLL_A_ID or DPLL_A_PIN_ID, depending
if provides info on parent dpll or pin.

>
>>+			parent_present = true;
>>+			parent_idx = nla_get_u32(a);
>>+			break;
>>+		case DPLL_A_PIN_STATE:
>>+			state = nla_get_u8(a);
>>+			break;
>>+		default:
>>+			break;
>>+		}
>
>
>Why do you have to iterate here? Why simple
>	if (attr_x)
>		ret = dpll_pin_x_set()
>
>is not enough?
>
>Is it because of state? if yes:
>	if (attr_state)
>		if (attr_parent)
>			dpll_pin_on_pin_state_set()
>		else
>			dpll_pin_state_set()
>
>

Fixed.

>
>
>>+	}
>>+	if (state != DPLL_PIN_STATE_UNSPEC) {
>>+		if (!parent_present) {
>>+			ret = dpll_pin_state_set(dpll, pin, state,
>>+						 info->extack);
>>+			if (ret)
>>+				return ret;
>>+		} else {
>>+			ret = dpll_pin_on_pin_state_set(dpll, pin, parent_idx,
>>+							state, info->extack);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct dpll_pin *pin = info->user_ptr[1];
>>+
>>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>>+}
>>+
>>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_pin *pin = info->user_ptr[1];
>>+	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;
>>+}
>>+
>>+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 *attr;
>>+	enum dpll_mode mode;
>>+	int rem, ret = 0;
>>+
>>+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(attr)) {
>>+		case DPLL_A_MODE:
>
>Again, why loop? I don't see any sane reason, is there any?
>

No, no reason now, will remove it.

>
>
>>+			mode = nla_get_u8(attr);
>>+
>>+			ret = ops->mode_set(dpll, dpll_priv(dpll), mode,
>>+					    info->extack);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+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_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_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_xa_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_xa_lock);
>>+	return -ENODEV;
>>+}
>>+
>>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+		    struct genl_info *info)
>>+{
>>+	mutex_unlock(&dpll_xa_lock);
>>+}
>>+
>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_lock(&dpll_xa_lock);
>>+
>>+	return 0;
>>+}
>>+
>>+int dpll_post_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_unlock(&dpll_xa_lock);
>>+
>>+	return 0;
>>+}
>>+
>>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+		      struct genl_info *info)
>>+{
>>+	int ret = dpll_pre_doit(ops, skb, info);
>>+	struct dpll_pin_ref *pin_ref;
>>+	struct dpll_device *dpll;
>>+
>>+	if (ret)
>>+		return ret;
>>+	dpll = info->user_ptr[0];
>>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>>+		ret = -EINVAL;
>>+		goto unlock_dev;
>>+	}
>>+	pin_ref = xa_load(&dpll->pin_refs,
>>+			  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>
>This is inconsistent, also incorrect.
>
>You use DPLL_A_ID that is stored in dpll_device_xa as a handle for device.
>That is fine if we consider Jakub's desire to have this randomly
>generated id as a handle (I find is questinable, but can live with it).
>
>But pins are independent on a single DPLL and could be attached to
>multiple ones. Using a single DPLL_A_ID as handle here (dpll_pre_doit)
>for all operations is plain wrong.
>
>For example for frequency or direction set, you don't need it in code as
>you iterate over all attacheds DPLL devices. Confusing to require DPLL
>device handle for that operation when you change setup for all of them.
>That is wrong.
>
>Also, you have global dpll_pin_xa. Yet you don't expose this ID over
>netlink. To be consistent with device handle, you should:
>1) expose pin->id over DPLL_A_PIN_ID
>2) use this DPLL_A_PIN_ID as a sole pin handle for dpll_pin_xa lookup.
>
>For DPLL-PIN tuple operations (prio_set and state_on_dpll_set)
>you should process the dpll device handle (DPLL_A_ID) where it is needed
>In the similar way you process parent id now where is it needed
>(state_on_pin_set)
>
>For GET/DUMP command, this does not also make sense.
>Check out __dpll_cmd_pin_dump_one()
>
>You just use the "first dpll" for the handle. Just use the pin->id as I
>suggested above.
>
>Makes sense?
>
>Please make sure you maintain the same handle attrs in the notification
>messages as well.
>
>

Yeah, with the changes to the other thread, this works now as you described.

>
>>+	if (!pin_ref) {
>>+		ret = -ENODEV;
>>+		goto unlock_dev;
>>+	}
>>+	info->user_ptr[1] = pin_ref->pin;
>>+
>>+	return 0;
>>+
>>+unlock_dev:
>>+	mutex_unlock(&dpll_xa_lock);
>>+	return ret;
>>+}
>>+
>>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff
>>*skb,
>>+			struct genl_info *info)
>>+{
>>+	dpll_post_doit(ops, skb, info);
>>+}
>>+
>>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	return dpll_pre_dumpit(cb);
>>+}
>>+
>>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>+{
>>+	return dpll_post_dumpit(cb);
>>+}
>>+
>>+static int
>>+dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct dpll_pin *parent,
>>+			 enum dplla attr)
>>+{
>>+	int ret = dpll_msg_add_dev_handle(msg, dpll);
>>+	struct dpll_pin_ref *ref = NULL;
>
>Pointless init.
>

Fixed.

>
>>+	enum dpll_pin_state state;
>>+
>>+	if (ret)
>>+		return ret;
>>+	if (pin && nla_put_u32(msg, DPLL_A_PIN_IDX, pin->pin_idx))
>>+		return -EMSGSIZE;
>>+
>>+	switch (attr) {
>>+	case DPLL_A_MODE:
>>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_LOCK_STATUS:
>>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_TEMP:
>>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>>+		break;
>>+	case DPLL_A_PIN_FREQUENCY:
>>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+		if (!ref)
>>+			return -EFAULT;
>>+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
>>+		break;
>>+	case DPLL_A_PIN_PRIO:
>>+		ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+		if (!ref)
>>+			return -EFAULT;
>>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>
>Why exactly did you ignore my request I put in the previous version
>review asking to maintain the same nesting scheme for GET cmd and
>notification messages? Honestly, the silent ignores I'm getting
>all along the review of this patchset is very frustrating. Please don't
>do it. Either ack and change or provide exaplanation why your code is
>fine.
>
>So could you please fix this?
>Again, please make sure that the notification messages have attributes
>in exactly the same place as GET cmd (think of it as the rest of the
>attrs in the message is filtered out). Makes possible to use the same
>userspace parsing code both messages.
>
>

Fixed.

>
>>+		break;
>>+	case DPLL_A_PIN_STATE:
>>+		if (parent) {
>>+			const struct dpll_pin_ops *ops;
>>+			void *priv = dpll_pin_on_pin_priv(parent, pin);
>>+
>>+			ref = xa_load(&pin->parent_refs, parent->pin_idx);
>>+			if (!ref)
>>+				return -EFAULT;
>>+			ops = dpll_pin_ops(ref);
>>+			if (!ops->state_on_pin_get)
>>+				return -EOPNOTSUPP;
>>+			ret = ops->state_on_pin_get(pin, priv, parent,
>>+						    &state, NULL);
>>+			if (ret)
>>+				return ret;
>>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>+					parent->pin_idx))
>>+				return -EMSGSIZE;
>>+		} else {
>>+			ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+			if (!ref)
>>+				return -EFAULT;
>>+			ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
>>+							     NULL);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_send_event_create(enum dpll_event event, struct dpll_device *dpll)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	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_msg_add_dev_handle(msg, dpll);
>>+	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;
>>+}
>>+
>>+static int
>>+dpll_send_event_change(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		       struct dpll_pin *parent, enum dplla attr)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0,
>>+			  DPLL_EVENT_DEVICE_CHANGE);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_change(msg, dpll, pin, parent, attr);
>>+	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_notify_device_create(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>
>Quite odd. Consider rename of dpll_send_event_create()
>

This has already changed.

>
>>+}
>>+
>>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr)
>>+{
>>+	if (WARN_ON(!dpll))
>>+		return -EINVAL;
>>+
>>+	return dpll_send_event_change(dpll, NULL, NULL, attr);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dplla attr)
>>+{
>>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>>+
>>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin
>>*pin,
>>+			   struct dpll_pin *parent, enum dplla attr)
>>+{
>>+	return dpll_send_event_change(dpll, pin, parent, attr);
>>+}
>>+
>>+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..952e0335595e
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.h
>>@@ -0,0 +1,27 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+/**
>>+ * dpll_notify_device_create - notify that the device has been created
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_create(struct dpll_device *dpll);
>>+
>>+
>>+/**
>>+ * dpll_notify_device_delete - notify that the device has been deleted
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_delete(struct dpll_device *dpll);
>>+
>>+int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			   struct dpll_pin *parent, enum dplla attr);
>>+
>>+int __init dpll_netlink_init(void);
>>+void dpll_netlink_finish(void);
>>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>>new file mode 100644
>>index 000000000000..5194efaf55a8
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,274 @@
>>+/* 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,
>>+				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,
>>+				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 *label;
>>+	enum dpll_pin_type type;
>>+	unsigned long capabilities;
>>+	u32 freq_supported_num;
>>+	struct dpll_pin_frequency *freq_supported;
>>+};
>>+
>>+/**
>>+ * dpll_device_get - find or create dpll_device object
>>+ * @clock_id: a system unique number for a device
>>+ * @dev_driver_id: index of dpll device on parent device
>>+ * @module: register module
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized dpll - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_device
>>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>>+
>>+/**
>>+ * dpll_device_put - caller drops reference to the device, free resources
>>+ * @dpll: dpll device pointer
>>+ *
>>+ * If all dpll_device_get callers drops their reference, the dpll device
>>+ * resources are freed.
>>+ */
>>+void dpll_device_put(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_device_register - register device, make it visible in the
>>subsystem.
>>+ * @dpll: reference previously allocated with dpll_device_get
>>+ * @type: type of dpll
>>+ * @ops: callbacks
>>+ * @priv: private data of registerer
>>+ * @owner: device struct of the owner
>>+ *
>>+ */
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+			 const struct dpll_device_ops *ops, void *priv,
>>+			 struct device *owner);
>>+
>>+/**
>>+ * dpll_device_unregister - deregister registered dpll
>>+ * @dpll: pointer to dpll
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>>+ * API users.
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+			    const struct dpll_device_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_get - get reference or create new pin object
>>+ * @clock_id: a system unique number of a device
>>+ * @@dev_driver_id: index of dpll device on parent device
>>+ * @module: register module
>>+ * @pin_prop: constant properities of a pin
>>+ *
>>+ * find existing pin with given clock_id, @dev_driver_id and module, or
>>create new
>>+ * and returen its reference.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized pin - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_pin
>>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>>+	      const struct dpll_pin_properties *prop);
>>+
>>+/**
>>+ * dpll_pin_register - register pin with a dpll device
>>+ * @dpll: pointer to dpll object to register pin with
>>+ * @pin: pointer to allocated pin object being registered with dpll
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * Register previously allocated pin object with a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this dpll,
>>+ * * -EBUSY - couldn't allocate id for a pin.
>>+ */
>
>I don't follow. In one of the previous version reviews I asked if you
>can have the function docs only in one place, preferably in .c. But you
>have there here in .h as well. Why? They are inconsistent. Could you
>please remove them from .h?
>

Fixed.

>
>
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      const struct dpll_pin_ops *ops, void *priv,
>>+		      struct device *rclk_device);
>
>As I asked in the previous version, could you please remove this
>rclk_device
>pointer. This is replaced by my patch allowing to expose dpll pin for
>netdev over RTnetlink.
>
>

Fixed.

>
>>+
>>+/**
>>+ * dpll_pin_unregister - deregister pin from a dpll device
>>+ * @dpll: pointer to dpll object to deregister pin from
>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>+ * @ops: ops for a dpll pin ops
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Deregister previously registered pin object from a dpll device.
>>+ *
>>+ */
>>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>>+			 const struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_put - drop reference to a pin acquired with dpll_pin_get
>>+ * @pin: pointer to allocated pin
>>+ *
>>+ * Pins shall be deregistered from all dpll devices before putting them,
>>+ * otherwise the memory won't be freed.
>>+ */
>>+void dpll_pin_put(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>>+ * @parent: parent pin pointer
>>+ * @pin: pointer to allocated pin object being registered with a parent pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ */
>>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>>+			     const struct dpll_pin_ops *ops, void *priv,
>>+			     struct device *rclk_device);
>>+
>>+/**
>>+ * dpll_pin_on_pin_register - register a pin to a muxed-type pin
>>+ * @parent: parent pin pointer
>>+ * @pin: pointer to allocated pin object being registered with a parent pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ * @rclk_device: pointer to device struct if pin is used for recovery of
>>a clock
>>+ * from that device
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ */
>>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin
>>*pin,
>>+				const struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_device_notify - notify on dpll device change
>>+ * @dpll: dpll device pointer
>>+ * @attr: changed attribute
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_device_notify(struct dpll_device *dpll, enum dplla attr);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dplla attr);
>>+
>>+
>>+
>>+#endif
>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>index e188bc189754..75eeaa4396eb 100644
>>--- a/include/uapi/linux/dpll.h
>>+++ b/include/uapi/linux/dpll.h
>>@@ -111,6 +111,8 @@ enum dpll_pin_direction {
>>
>> #define DPLL_PIN_FREQUENCY_1_HZ		1
>> #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>>+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>>+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
>>
>> /**
>>  * enum dpll_pin_state - defines possible states of a pin, valid values for
>>--
>>2.34.1
>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-03  8:09     ` Jiri Pirko
@ 2023-06-06 18:50       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:50 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 3, 2023 10:09 AM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>[...]
>
>
>>+static struct dpll_pin *
>>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>>+	       const struct dpll_pin_properties *prop)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret, fs_size;
>>+
>>+	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;
>>+	refcount_set(&pin->refcount, 1);
>>+	if (WARN_ON(!prop->label)) {
>
>Why exactly label has to be mandatory? In mlx5, I have no use for it.
>Please make it optional. IIRC, I asked for this in the last review
>as well.
>

Fixed.

>
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>
>Labels should be static const string. Do you see a usecase when you need
>to dup it? If not, remove this please.
>

Fixed.

Thank you,
Arkadiusz
>
>
>>+	if (!pin->prop.label) {
>>+		ret = -ENOMEM;
>>+		goto err;
>>+	}
>
>
>[...]

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-06 18:50       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:50 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, May 3, 2023 10:09 AM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>[...]
>
>
>>+static struct dpll_pin *
>>+dpll_pin_alloc(u64 clock_id, u8 pin_idx, struct module *module,
>>+	       const struct dpll_pin_properties *prop)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret, fs_size;
>>+
>>+	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;
>>+	refcount_set(&pin->refcount, 1);
>>+	if (WARN_ON(!prop->label)) {
>
>Why exactly label has to be mandatory? In mlx5, I have no use for it.
>Please make it optional. IIRC, I asked for this in the last review
>as well.
>

Fixed.

>
>>+		ret = -EINVAL;
>>+		goto err;
>>+	}
>>+	pin->prop.label = kstrdup(prop->label, GFP_KERNEL);
>
>Labels should be static const string. Do you see a usecase when you need
>to dup it? If not, remove this please.
>

Fixed.

Thank you,
Arkadiusz
>
>
>>+	if (!pin->prop.label) {
>>+		ret = -ENOMEM;
>>+		goto err;
>>+	}
>
>
>[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-04 20:27       ` Jakub Kicinski
@ 2023-06-06 18:55         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:55 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech, Milena, Michalik,
	Michal, linux-arm-kernel, Vadim Fedorenko, poros, mschmidt,
	netdev, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 4, 2023 10:28 PM
>
>On Thu, 4 May 2023 13:18:49 +0200 Jiri Pirko wrote:
>> >diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>> >index e188bc189754..75eeaa4396eb 100644
>> >--- a/include/uapi/linux/dpll.h
>> >+++ b/include/uapi/linux/dpll.h
>> >@@ -111,6 +111,8 @@ enum dpll_pin_direction {
>> >
>> > #define DPLL_PIN_FREQUENCY_1_HZ		1
>> > #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>> >+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>> >+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
>>
>> This should be moved to patch #1.
>> please convert to enum, could be unnamed.
>
>+1, you can't edit the YNL-generated files at all.

Fixed.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-06 18:55         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-06 18:55 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech, Milena, Michalik,
	Michal, linux-arm-kernel, Vadim Fedorenko, poros, mschmidt,
	netdev, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 4, 2023 10:28 PM
>
>On Thu, 4 May 2023 13:18:49 +0200 Jiri Pirko wrote:
>> >diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>> >index e188bc189754..75eeaa4396eb 100644
>> >--- a/include/uapi/linux/dpll.h
>> >+++ b/include/uapi/linux/dpll.h
>> >@@ -111,6 +111,8 @@ enum dpll_pin_direction {
>> >
>> > #define DPLL_PIN_FREQUENCY_1_HZ		1
>> > #define DPLL_PIN_FREQUENCY_10_MHZ	10000000
>> >+#define DPLL_PIN_FREQUENCY_10_KHZ	10000
>> >+#define DPLL_PIN_FREQUENCY_77_5_KHZ	77500
>>
>> This should be moved to patch #1.
>> please convert to enum, could be unnamed.
>
>+1, you can't edit the YNL-generated files at all.

Fixed.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-22 14:45     ` Paolo Abeni
@ 2023-06-09 12:51       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:51 UTC (permalink / raw)
  To: Paolo Abeni, Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon
  Cc: Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Monday, May 22, 2023 4:45 PM
>
>On Thu, 2023-04-27 at 17:20 -0700, Vadim Fedorenko wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> 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.
>>
>> Co-developed-by: Milena Olech <milena.olech@intel.com>
>> Signed-off-by: Milena Olech <milena.olech@intel.com>
>> Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>> Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>> Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>As this patch is quite big and tend to accumulate quite a few comments
>- both hard to track and to address - I'm wondering if splitting it in
>a few separated patches would could help?
>
>e.g.
>
>- 1 patch for dpll device struct && APIs definition
>- 1 patch for pin related APIs
>- 1 patch for netlink notification.
>
>(to be considered only if the effort for the above split is not
>overwhelming)
>
>Possibly the same could apply to patch 5/8.
>
>Cheers,
>
>Paolo

Sure, I split this patch for v8 (only separating the outcome object
files and header for easier review), but didn't managed to split the
ice patch yet, hopefully will manage this for next submission.

Thank you!
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-09 12:51       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:51 UTC (permalink / raw)
  To: Paolo Abeni, Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon
  Cc: Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Monday, May 22, 2023 4:45 PM
>
>On Thu, 2023-04-27 at 17:20 -0700, Vadim Fedorenko wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> 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.
>>
>> Co-developed-by: Milena Olech <milena.olech@intel.com>
>> Signed-off-by: Milena Olech <milena.olech@intel.com>
>> Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>> Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>> Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>As this patch is quite big and tend to accumulate quite a few comments
>- both hard to track and to address - I'm wondering if splitting it in
>a few separated patches would could help?
>
>e.g.
>
>- 1 patch for dpll device struct && APIs definition
>- 1 patch for pin related APIs
>- 1 patch for netlink notification.
>
>(to be considered only if the effort for the above split is not
>overwhelming)
>
>Possibly the same could apply to patch 5/8.
>
>Cheers,
>
>Paolo

Sure, I split this patch for v8 (only separating the outcome object
files and header for easier review), but didn't managed to split the
ice patch yet, hopefully will manage this for next submission.

Thank you!
Arkadiusz
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-09 13:40   ` Jiri Pirko
@ 2023-06-09 12:53       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:53 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 9, 2023 3:40 PM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>
>[...]
>
>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_lock(&dpll_xa_lock);
>
>Did you test this?
>
>I'm gettting following deadlock warning:
>
>[  280.899789] ======================================================
>[  280.900458] WARNING: possible circular locking dependency detected
>[  280.901126] 6.3.0jiri+ #4 Tainted: G             L
>[  280.901702] ------------------------------------------------------
>[  280.902378] python3/1058 is trying to acquire lock:
>[  280.902934] ffff88811571ae88 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}, at:
>netlink_dump+0x4a/0x400
>[  280.903869]
>               but task is already holding lock:
>[  280.904559] ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>dpll_pin_pre_dumpit+0x13/0x20
>[  280.905464]
>               which lock already depends on the new lock.
>
>[  280.906414]
>               the existing dependency chain (in reverse order) is:
>[  280.907141]
>               -> #1 (dpll_xa_lock){+.+.}-{3:3}:
>[  280.907711]        __mutex_lock+0x91/0xbb0
>[  280.908116]        dpll_pin_pre_dumpit+0x13/0x20
>[  280.908553]        genl_start+0xc6/0x150
>[  280.908940]        __netlink_dump_start+0x158/0x230
>[  280.909399]        genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.909894]        genl_rcv_msg+0x115/0x290
>[  280.910302]        netlink_rcv_skb+0x54/0x100
>[  280.910726]        genl_rcv+0x24/0x40
>[  280.911106]        netlink_unicast+0x182/0x260
>[  280.911547]        netlink_sendmsg+0x242/0x4b0
>[  280.911984]        sock_sendmsg+0x38/0x60
>[  280.912384]        __sys_sendto+0xeb/0x130
>[  280.912797]        __x64_sys_sendto+0x20/0x30
>[  280.913227]        do_syscall_64+0x3c/0x80
>[  280.913639]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.914156]
>               -> #0 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}:
>[  280.914809]        __lock_acquire+0x1165/0x26b0
>[  280.915254]        lock_acquire+0xce/0x2b0
>[  280.915665]        __mutex_lock+0x91/0xbb0
>[  280.916080]        netlink_dump+0x4a/0x400
>[  280.916488]        __netlink_dump_start+0x188/0x230
>[  280.916953]        genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.917448]        genl_rcv_msg+0x115/0x290
>[  280.917863]        netlink_rcv_skb+0x54/0x100
>[  280.918301]        genl_rcv+0x24/0x40
>[  280.918686]        netlink_unicast+0x182/0x260
>[  280.919129]        netlink_sendmsg+0x242/0x4b0
>[  280.919569]        sock_sendmsg+0x38/0x60
>[  280.919969]        __sys_sendto+0xeb/0x130
>[  280.920377]        __x64_sys_sendto+0x20/0x30
>[  280.920808]        do_syscall_64+0x3c/0x80
>[  280.921220]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.921730]
>               other info that might help us debug this:
>
>[  280.922513]  Possible unsafe locking scenario:
>
>[  280.923095]        CPU0                    CPU1
>[  280.923541]        ----                    ----
>[  280.923976]   lock(dpll_xa_lock);
>[  280.924329]                                lock(nlk_cb_mutex-GENERIC);
>[  280.924916]                                lock(dpll_xa_lock);
>[  280.925454]   lock(nlk_cb_mutex-GENERIC);
>[  280.925858]
>                *** DEADLOCK ***
>
>[  280.926488] 2 locks held by python3/1058:
>[  280.926891]  #0: ffffffff827e2430 (cb_lock){++++}-{3:3}, at:
>genl_rcv+0x15/0x40
>[  280.927585]  #1: ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>dpll_pin_pre_dumpit+0x13/0x20
>[  280.928385]
>               stack backtrace:
>[  280.928853] CPU: 8 PID: 1058 Comm: python3 Tainted: G             L
>6.3.0jiri+ #4
>[  280.929586] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
>rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
>[  280.930558] Call Trace:
>[  280.930849]  <TASK>
>[  280.931117]  dump_stack_lvl+0x58/0xb0
>[  280.931500]  check_noncircular+0x11b/0x130
>[  280.931916]  ? kernel_text_address+0x109/0x110
>[  280.932353]  __lock_acquire+0x1165/0x26b0
>[  280.932759]  lock_acquire+0xce/0x2b0
>[  280.933130]  ? netlink_dump+0x4a/0x400
>[  280.933517]  __mutex_lock+0x91/0xbb0
>[  280.933885]  ? netlink_dump+0x4a/0x400
>[  280.934269]  ? netlink_dump+0x4a/0x400
>[  280.934662]  ? netlink_dump+0x4a/0x400
>[  280.935054]  netlink_dump+0x4a/0x400
>[  280.935426]  __netlink_dump_start+0x188/0x230
>[  280.935857]  genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.936321]  ? genl_family_rcv_msg_attrs_parse.constprop.0+0xe0/0xe0
>[  280.936887]  ? dpll_nl_pin_get_doit+0x100/0x100
>[  280.937324]  ? genl_lock_dumpit+0x50/0x50
>[  280.937729]  genl_rcv_msg+0x115/0x290
>[  280.938109]  ? dpll_pin_post_doit+0x20/0x20
>[  280.938526]  ? dpll_nl_pin_get_doit+0x100/0x100
>[  280.938966]  ? dpll_pin_pre_dumpit+0x20/0x20
>[  280.939390]  ? genl_family_rcv_msg_doit.isra.0+0x110/0x110
>[  280.939904]  netlink_rcv_skb+0x54/0x100
>[  280.940296]  genl_rcv+0x24/0x40
>[  280.940636]  netlink_unicast+0x182/0x260
>[  280.941034]  netlink_sendmsg+0x242/0x4b0
>[  280.941439]  sock_sendmsg+0x38/0x60
>[  280.941804]  ? sockfd_lookup_light+0x12/0x70
>[  280.942230]  __sys_sendto+0xeb/0x130
>[  280.942616]  ? mntput_no_expire+0x7e/0x490
>[  280.943038]  ? proc_nr_files+0x30/0x30
>[  280.943425]  __x64_sys_sendto+0x20/0x30
>[  280.943817]  do_syscall_64+0x3c/0x80
>[  280.944194]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.944674] RIP: 0033:0x7f252fd132b0
>[  280.945042] Code: c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64
>8b 04 25 18 00 00 00 85 c0 75 1d 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05
><48> 3d 00 f0 ff ff 77 68 c3 0f 1f 80 00 00 00 00 41 54 48 83 ec 20
>[  280.946622] RSP: 002b:00007ffdbd9335d8 EFLAGS: 00000246 ORIG_RAX:
>000000000000002c
>[  280.947328] RAX: ffffffffffffffda RBX: 00007ffdbd933688 RCX:
>00007f252fd132b0
>[  280.947962] RDX: 0000000000000014 RSI: 00007f252ede65d0 RDI:
>0000000000000003
>[  280.948594] RBP: 00007f252f806da0 R08: 0000000000000000 R09:
>0000000000000000
>[  280.949229] R10: 0000000000000000 R11: 0000000000000246 R12:
>0000000000000000
>[  280.949858] R13: ffffffffc4653600 R14: 0000000000000001 R15:
>00007f252f74d147
>[  280.950494]  </TASK>
>
>Problem is that in __netlink_dump_start() you take dpll_xa_lock
>(in control->start(cb)) while holding nlk->cb_mutex, then you unlock
>the nlk->cb_mutex and take it again in netlink_dump().
>I hear "Chiquitita" from the distance :)
>
>[...]

Well I tested it, but haven't seen such outcome, do you have any script
for reproducing this behavior?

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-09 12:53       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:53 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, Vadim Fedorenko, poros,
	mschmidt, netdev, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, May 9, 2023 3:40 PM
>
>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>
>[...]
>
>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>+{
>>+	mutex_lock(&dpll_xa_lock);
>
>Did you test this?
>
>I'm gettting following deadlock warning:
>
>[  280.899789] ======================================================
>[  280.900458] WARNING: possible circular locking dependency detected
>[  280.901126] 6.3.0jiri+ #4 Tainted: G             L
>[  280.901702] ------------------------------------------------------
>[  280.902378] python3/1058 is trying to acquire lock:
>[  280.902934] ffff88811571ae88 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}, at:
>netlink_dump+0x4a/0x400
>[  280.903869]
>               but task is already holding lock:
>[  280.904559] ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>dpll_pin_pre_dumpit+0x13/0x20
>[  280.905464]
>               which lock already depends on the new lock.
>
>[  280.906414]
>               the existing dependency chain (in reverse order) is:
>[  280.907141]
>               -> #1 (dpll_xa_lock){+.+.}-{3:3}:
>[  280.907711]        __mutex_lock+0x91/0xbb0
>[  280.908116]        dpll_pin_pre_dumpit+0x13/0x20
>[  280.908553]        genl_start+0xc6/0x150
>[  280.908940]        __netlink_dump_start+0x158/0x230
>[  280.909399]        genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.909894]        genl_rcv_msg+0x115/0x290
>[  280.910302]        netlink_rcv_skb+0x54/0x100
>[  280.910726]        genl_rcv+0x24/0x40
>[  280.911106]        netlink_unicast+0x182/0x260
>[  280.911547]        netlink_sendmsg+0x242/0x4b0
>[  280.911984]        sock_sendmsg+0x38/0x60
>[  280.912384]        __sys_sendto+0xeb/0x130
>[  280.912797]        __x64_sys_sendto+0x20/0x30
>[  280.913227]        do_syscall_64+0x3c/0x80
>[  280.913639]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.914156]
>               -> #0 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}:
>[  280.914809]        __lock_acquire+0x1165/0x26b0
>[  280.915254]        lock_acquire+0xce/0x2b0
>[  280.915665]        __mutex_lock+0x91/0xbb0
>[  280.916080]        netlink_dump+0x4a/0x400
>[  280.916488]        __netlink_dump_start+0x188/0x230
>[  280.916953]        genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.917448]        genl_rcv_msg+0x115/0x290
>[  280.917863]        netlink_rcv_skb+0x54/0x100
>[  280.918301]        genl_rcv+0x24/0x40
>[  280.918686]        netlink_unicast+0x182/0x260
>[  280.919129]        netlink_sendmsg+0x242/0x4b0
>[  280.919569]        sock_sendmsg+0x38/0x60
>[  280.919969]        __sys_sendto+0xeb/0x130
>[  280.920377]        __x64_sys_sendto+0x20/0x30
>[  280.920808]        do_syscall_64+0x3c/0x80
>[  280.921220]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.921730]
>               other info that might help us debug this:
>
>[  280.922513]  Possible unsafe locking scenario:
>
>[  280.923095]        CPU0                    CPU1
>[  280.923541]        ----                    ----
>[  280.923976]   lock(dpll_xa_lock);
>[  280.924329]                                lock(nlk_cb_mutex-GENERIC);
>[  280.924916]                                lock(dpll_xa_lock);
>[  280.925454]   lock(nlk_cb_mutex-GENERIC);
>[  280.925858]
>                *** DEADLOCK ***
>
>[  280.926488] 2 locks held by python3/1058:
>[  280.926891]  #0: ffffffff827e2430 (cb_lock){++++}-{3:3}, at:
>genl_rcv+0x15/0x40
>[  280.927585]  #1: ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>dpll_pin_pre_dumpit+0x13/0x20
>[  280.928385]
>               stack backtrace:
>[  280.928853] CPU: 8 PID: 1058 Comm: python3 Tainted: G             L
>6.3.0jiri+ #4
>[  280.929586] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
>rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
>[  280.930558] Call Trace:
>[  280.930849]  <TASK>
>[  280.931117]  dump_stack_lvl+0x58/0xb0
>[  280.931500]  check_noncircular+0x11b/0x130
>[  280.931916]  ? kernel_text_address+0x109/0x110
>[  280.932353]  __lock_acquire+0x1165/0x26b0
>[  280.932759]  lock_acquire+0xce/0x2b0
>[  280.933130]  ? netlink_dump+0x4a/0x400
>[  280.933517]  __mutex_lock+0x91/0xbb0
>[  280.933885]  ? netlink_dump+0x4a/0x400
>[  280.934269]  ? netlink_dump+0x4a/0x400
>[  280.934662]  ? netlink_dump+0x4a/0x400
>[  280.935054]  netlink_dump+0x4a/0x400
>[  280.935426]  __netlink_dump_start+0x188/0x230
>[  280.935857]  genl_family_rcv_msg_dumpit+0xf9/0x110
>[  280.936321]  ? genl_family_rcv_msg_attrs_parse.constprop.0+0xe0/0xe0
>[  280.936887]  ? dpll_nl_pin_get_doit+0x100/0x100
>[  280.937324]  ? genl_lock_dumpit+0x50/0x50
>[  280.937729]  genl_rcv_msg+0x115/0x290
>[  280.938109]  ? dpll_pin_post_doit+0x20/0x20
>[  280.938526]  ? dpll_nl_pin_get_doit+0x100/0x100
>[  280.938966]  ? dpll_pin_pre_dumpit+0x20/0x20
>[  280.939390]  ? genl_family_rcv_msg_doit.isra.0+0x110/0x110
>[  280.939904]  netlink_rcv_skb+0x54/0x100
>[  280.940296]  genl_rcv+0x24/0x40
>[  280.940636]  netlink_unicast+0x182/0x260
>[  280.941034]  netlink_sendmsg+0x242/0x4b0
>[  280.941439]  sock_sendmsg+0x38/0x60
>[  280.941804]  ? sockfd_lookup_light+0x12/0x70
>[  280.942230]  __sys_sendto+0xeb/0x130
>[  280.942616]  ? mntput_no_expire+0x7e/0x490
>[  280.943038]  ? proc_nr_files+0x30/0x30
>[  280.943425]  __x64_sys_sendto+0x20/0x30
>[  280.943817]  do_syscall_64+0x3c/0x80
>[  280.944194]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
>[  280.944674] RIP: 0033:0x7f252fd132b0
>[  280.945042] Code: c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64
>8b 04 25 18 00 00 00 85 c0 75 1d 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05
><48> 3d 00 f0 ff ff 77 68 c3 0f 1f 80 00 00 00 00 41 54 48 83 ec 20
>[  280.946622] RSP: 002b:00007ffdbd9335d8 EFLAGS: 00000246 ORIG_RAX:
>000000000000002c
>[  280.947328] RAX: ffffffffffffffda RBX: 00007ffdbd933688 RCX:
>00007f252fd132b0
>[  280.947962] RDX: 0000000000000014 RSI: 00007f252ede65d0 RDI:
>0000000000000003
>[  280.948594] RBP: 00007f252f806da0 R08: 0000000000000000 R09:
>0000000000000000
>[  280.949229] R10: 0000000000000000 R11: 0000000000000246 R12:
>0000000000000000
>[  280.949858] R13: ffffffffc4653600 R14: 0000000000000001 R15:
>00007f252f74d147
>[  280.950494]  </TASK>
>
>Problem is that in __netlink_dump_start() you take dpll_xa_lock
>(in control->start(cb)) while holding nlk->cb_mutex, then you unlock
>the nlk->cb_mutex and take it again in netlink_dump().
>I hear "Chiquitita" from the distance :)
>
>[...]

Well I tested it, but haven't seen such outcome, do you have any script
for reproducing this behavior?

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-05-04 21:21     ` Jakub Kicinski
@ 2023-06-09 12:54       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:54 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech, Milena, Michalik,
	Michal, linux-arm-kernel, Vadim Fedorenko, poros, mschmidt,
	netdev, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 4, 2023 11:21 PM
>
>On Thu, 27 Apr 2023 17:20:03 -0700 Vadim Fedorenko wrote:
>> +/**
>> + * struct dpll_pin - structure for a dpll pin
>> + * @idx:		unique idx given by alloc on global pin's XA
>> + * @dev_driver_id:	id given by dev driver
>> + * @clock_id:		clock_id of creator
>> + * @module:		module of creator
>> + * @dpll_refs:		hold referencees to dplls that pin is registered
>with
>> + * @pin_refs:		hold references to pins that pin is registered with
>> + * @prop:		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;
>> +	struct dpll_pin_properties prop;
>> +	char *rclk_dev_name;
>> +	refcount_t refcount;
>> +};
>
>The kdoc for structures is quite out of date, please run
>./scripts/kernel-doc -none $DPLL_FILES
>

Done.

>> +/**
>> + * dpll_device_notify - notify on dpll device change
>> + * @dpll: dpll device pointer
>> + * @attr: changed attribute
>> + *
>> + * Broadcast event to the netlink multicast registered listeners.
>> + *
>> + * Return:
>> + * * 0 - success
>> + * * negative - error
>> + */
>
>Let's move the kdoc to the implementation. I believe that's
>the preferred kernel coding style.

Sure, fixed.

Thank you,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

* RE: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-09 12:54       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 149+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-06-09 12:54 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech, Milena, Michalik,
	Michal, linux-arm-kernel, Vadim Fedorenko, poros, mschmidt,
	netdev, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, May 4, 2023 11:21 PM
>
>On Thu, 27 Apr 2023 17:20:03 -0700 Vadim Fedorenko wrote:
>> +/**
>> + * struct dpll_pin - structure for a dpll pin
>> + * @idx:		unique idx given by alloc on global pin's XA
>> + * @dev_driver_id:	id given by dev driver
>> + * @clock_id:		clock_id of creator
>> + * @module:		module of creator
>> + * @dpll_refs:		hold referencees to dplls that pin is registered
>with
>> + * @pin_refs:		hold references to pins that pin is registered with
>> + * @prop:		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;
>> +	struct dpll_pin_properties prop;
>> +	char *rclk_dev_name;
>> +	refcount_t refcount;
>> +};
>
>The kdoc for structures is quite out of date, please run
>./scripts/kernel-doc -none $DPLL_FILES
>

Done.

>> +/**
>> + * dpll_device_notify - notify on dpll device change
>> + * @dpll: dpll device pointer
>> + * @attr: changed attribute
>> + *
>> + * Broadcast event to the netlink multicast registered listeners.
>> + *
>> + * Return:
>> + * * 0 - success
>> + * * negative - error
>> + */
>
>Let's move the kdoc to the implementation. I believe that's
>the preferred kernel coding style.

Sure, fixed.

Thank you,
Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
  2023-06-09 12:53       ` Kubalewski, Arkadiusz
@ 2023-06-13 13:49         ` Jiri Pirko
  -1 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-06-13 13:49 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Jun 09, 2023 at 02:53:27PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 9, 2023 3:40 PM
>>
>>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>>
>>
>>[...]
>>
>>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>>+{
>>>+	mutex_lock(&dpll_xa_lock);
>>
>>Did you test this?
>>
>>I'm gettting following deadlock warning:
>>
>>[  280.899789] ======================================================
>>[  280.900458] WARNING: possible circular locking dependency detected
>>[  280.901126] 6.3.0jiri+ #4 Tainted: G             L
>>[  280.901702] ------------------------------------------------------
>>[  280.902378] python3/1058 is trying to acquire lock:
>>[  280.902934] ffff88811571ae88 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}, at:
>>netlink_dump+0x4a/0x400
>>[  280.903869]
>>               but task is already holding lock:
>>[  280.904559] ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>>dpll_pin_pre_dumpit+0x13/0x20
>>[  280.905464]
>>               which lock already depends on the new lock.
>>
>>[  280.906414]
>>               the existing dependency chain (in reverse order) is:
>>[  280.907141]
>>               -> #1 (dpll_xa_lock){+.+.}-{3:3}:
>>[  280.907711]        __mutex_lock+0x91/0xbb0
>>[  280.908116]        dpll_pin_pre_dumpit+0x13/0x20
>>[  280.908553]        genl_start+0xc6/0x150
>>[  280.908940]        __netlink_dump_start+0x158/0x230
>>[  280.909399]        genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.909894]        genl_rcv_msg+0x115/0x290
>>[  280.910302]        netlink_rcv_skb+0x54/0x100
>>[  280.910726]        genl_rcv+0x24/0x40
>>[  280.911106]        netlink_unicast+0x182/0x260
>>[  280.911547]        netlink_sendmsg+0x242/0x4b0
>>[  280.911984]        sock_sendmsg+0x38/0x60
>>[  280.912384]        __sys_sendto+0xeb/0x130
>>[  280.912797]        __x64_sys_sendto+0x20/0x30
>>[  280.913227]        do_syscall_64+0x3c/0x80
>>[  280.913639]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.914156]
>>               -> #0 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}:
>>[  280.914809]        __lock_acquire+0x1165/0x26b0
>>[  280.915254]        lock_acquire+0xce/0x2b0
>>[  280.915665]        __mutex_lock+0x91/0xbb0
>>[  280.916080]        netlink_dump+0x4a/0x400
>>[  280.916488]        __netlink_dump_start+0x188/0x230
>>[  280.916953]        genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.917448]        genl_rcv_msg+0x115/0x290
>>[  280.917863]        netlink_rcv_skb+0x54/0x100
>>[  280.918301]        genl_rcv+0x24/0x40
>>[  280.918686]        netlink_unicast+0x182/0x260
>>[  280.919129]        netlink_sendmsg+0x242/0x4b0
>>[  280.919569]        sock_sendmsg+0x38/0x60
>>[  280.919969]        __sys_sendto+0xeb/0x130
>>[  280.920377]        __x64_sys_sendto+0x20/0x30
>>[  280.920808]        do_syscall_64+0x3c/0x80
>>[  280.921220]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.921730]
>>               other info that might help us debug this:
>>
>>[  280.922513]  Possible unsafe locking scenario:
>>
>>[  280.923095]        CPU0                    CPU1
>>[  280.923541]        ----                    ----
>>[  280.923976]   lock(dpll_xa_lock);
>>[  280.924329]                                lock(nlk_cb_mutex-GENERIC);
>>[  280.924916]                                lock(dpll_xa_lock);
>>[  280.925454]   lock(nlk_cb_mutex-GENERIC);
>>[  280.925858]
>>                *** DEADLOCK ***
>>
>>[  280.926488] 2 locks held by python3/1058:
>>[  280.926891]  #0: ffffffff827e2430 (cb_lock){++++}-{3:3}, at:
>>genl_rcv+0x15/0x40
>>[  280.927585]  #1: ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>>dpll_pin_pre_dumpit+0x13/0x20
>>[  280.928385]
>>               stack backtrace:
>>[  280.928853] CPU: 8 PID: 1058 Comm: python3 Tainted: G             L
>>6.3.0jiri+ #4
>>[  280.929586] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
>>rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
>>[  280.930558] Call Trace:
>>[  280.930849]  <TASK>
>>[  280.931117]  dump_stack_lvl+0x58/0xb0
>>[  280.931500]  check_noncircular+0x11b/0x130
>>[  280.931916]  ? kernel_text_address+0x109/0x110
>>[  280.932353]  __lock_acquire+0x1165/0x26b0
>>[  280.932759]  lock_acquire+0xce/0x2b0
>>[  280.933130]  ? netlink_dump+0x4a/0x400
>>[  280.933517]  __mutex_lock+0x91/0xbb0
>>[  280.933885]  ? netlink_dump+0x4a/0x400
>>[  280.934269]  ? netlink_dump+0x4a/0x400
>>[  280.934662]  ? netlink_dump+0x4a/0x400
>>[  280.935054]  netlink_dump+0x4a/0x400
>>[  280.935426]  __netlink_dump_start+0x188/0x230
>>[  280.935857]  genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.936321]  ? genl_family_rcv_msg_attrs_parse.constprop.0+0xe0/0xe0
>>[  280.936887]  ? dpll_nl_pin_get_doit+0x100/0x100
>>[  280.937324]  ? genl_lock_dumpit+0x50/0x50
>>[  280.937729]  genl_rcv_msg+0x115/0x290
>>[  280.938109]  ? dpll_pin_post_doit+0x20/0x20
>>[  280.938526]  ? dpll_nl_pin_get_doit+0x100/0x100
>>[  280.938966]  ? dpll_pin_pre_dumpit+0x20/0x20
>>[  280.939390]  ? genl_family_rcv_msg_doit.isra.0+0x110/0x110
>>[  280.939904]  netlink_rcv_skb+0x54/0x100
>>[  280.940296]  genl_rcv+0x24/0x40
>>[  280.940636]  netlink_unicast+0x182/0x260
>>[  280.941034]  netlink_sendmsg+0x242/0x4b0
>>[  280.941439]  sock_sendmsg+0x38/0x60
>>[  280.941804]  ? sockfd_lookup_light+0x12/0x70
>>[  280.942230]  __sys_sendto+0xeb/0x130
>>[  280.942616]  ? mntput_no_expire+0x7e/0x490
>>[  280.943038]  ? proc_nr_files+0x30/0x30
>>[  280.943425]  __x64_sys_sendto+0x20/0x30
>>[  280.943817]  do_syscall_64+0x3c/0x80
>>[  280.944194]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.944674] RIP: 0033:0x7f252fd132b0
>>[  280.945042] Code: c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64
>>8b 04 25 18 00 00 00 85 c0 75 1d 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05
>><48> 3d 00 f0 ff ff 77 68 c3 0f 1f 80 00 00 00 00 41 54 48 83 ec 20
>>[  280.946622] RSP: 002b:00007ffdbd9335d8 EFLAGS: 00000246 ORIG_RAX:
>>000000000000002c
>>[  280.947328] RAX: ffffffffffffffda RBX: 00007ffdbd933688 RCX:
>>00007f252fd132b0
>>[  280.947962] RDX: 0000000000000014 RSI: 00007f252ede65d0 RDI:
>>0000000000000003
>>[  280.948594] RBP: 00007f252f806da0 R08: 0000000000000000 R09:
>>0000000000000000
>>[  280.949229] R10: 0000000000000000 R11: 0000000000000246 R12:
>>0000000000000000
>>[  280.949858] R13: ffffffffc4653600 R14: 0000000000000001 R15:
>>00007f252f74d147
>>[  280.950494]  </TASK>
>>
>>Problem is that in __netlink_dump_start() you take dpll_xa_lock
>>(in control->start(cb)) while holding nlk->cb_mutex, then you unlock
>>the nlk->cb_mutex and take it again in netlink_dump().
>>I hear "Chiquitita" from the distance :)
>>
>>[...]
>
>Well I tested it, but haven't seen such outcome, do you have any script
>for reproducing this behavior?

Any dump will do. For example:
sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump device-get

This is still present in RFCv8.



>
>Thank you,
>Arkadiusz

^ permalink raw reply	[flat|nested] 149+ messages in thread

* Re: [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions
@ 2023-06-13 13:49         ` Jiri Pirko
  0 siblings, 0 replies; 149+ messages in thread
From: Jiri Pirko @ 2023-06-13 13:49 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-clk

Fri, Jun 09, 2023 at 02:53:27PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, May 9, 2023 3:40 PM
>>
>>Fri, Apr 28, 2023 at 02:20:03AM CEST, vadfed@meta.com wrote:
>>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>>
>>
>>[...]
>>
>>>+int dpll_pre_dumpit(struct netlink_callback *cb)
>>>+{
>>>+	mutex_lock(&dpll_xa_lock);
>>
>>Did you test this?
>>
>>I'm gettting following deadlock warning:
>>
>>[  280.899789] ======================================================
>>[  280.900458] WARNING: possible circular locking dependency detected
>>[  280.901126] 6.3.0jiri+ #4 Tainted: G             L
>>[  280.901702] ------------------------------------------------------
>>[  280.902378] python3/1058 is trying to acquire lock:
>>[  280.902934] ffff88811571ae88 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}, at:
>>netlink_dump+0x4a/0x400
>>[  280.903869]
>>               but task is already holding lock:
>>[  280.904559] ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>>dpll_pin_pre_dumpit+0x13/0x20
>>[  280.905464]
>>               which lock already depends on the new lock.
>>
>>[  280.906414]
>>               the existing dependency chain (in reverse order) is:
>>[  280.907141]
>>               -> #1 (dpll_xa_lock){+.+.}-{3:3}:
>>[  280.907711]        __mutex_lock+0x91/0xbb0
>>[  280.908116]        dpll_pin_pre_dumpit+0x13/0x20
>>[  280.908553]        genl_start+0xc6/0x150
>>[  280.908940]        __netlink_dump_start+0x158/0x230
>>[  280.909399]        genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.909894]        genl_rcv_msg+0x115/0x290
>>[  280.910302]        netlink_rcv_skb+0x54/0x100
>>[  280.910726]        genl_rcv+0x24/0x40
>>[  280.911106]        netlink_unicast+0x182/0x260
>>[  280.911547]        netlink_sendmsg+0x242/0x4b0
>>[  280.911984]        sock_sendmsg+0x38/0x60
>>[  280.912384]        __sys_sendto+0xeb/0x130
>>[  280.912797]        __x64_sys_sendto+0x20/0x30
>>[  280.913227]        do_syscall_64+0x3c/0x80
>>[  280.913639]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.914156]
>>               -> #0 (nlk_cb_mutex-GENERIC){+.+.}-{3:3}:
>>[  280.914809]        __lock_acquire+0x1165/0x26b0
>>[  280.915254]        lock_acquire+0xce/0x2b0
>>[  280.915665]        __mutex_lock+0x91/0xbb0
>>[  280.916080]        netlink_dump+0x4a/0x400
>>[  280.916488]        __netlink_dump_start+0x188/0x230
>>[  280.916953]        genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.917448]        genl_rcv_msg+0x115/0x290
>>[  280.917863]        netlink_rcv_skb+0x54/0x100
>>[  280.918301]        genl_rcv+0x24/0x40
>>[  280.918686]        netlink_unicast+0x182/0x260
>>[  280.919129]        netlink_sendmsg+0x242/0x4b0
>>[  280.919569]        sock_sendmsg+0x38/0x60
>>[  280.919969]        __sys_sendto+0xeb/0x130
>>[  280.920377]        __x64_sys_sendto+0x20/0x30
>>[  280.920808]        do_syscall_64+0x3c/0x80
>>[  280.921220]        entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.921730]
>>               other info that might help us debug this:
>>
>>[  280.922513]  Possible unsafe locking scenario:
>>
>>[  280.923095]        CPU0                    CPU1
>>[  280.923541]        ----                    ----
>>[  280.923976]   lock(dpll_xa_lock);
>>[  280.924329]                                lock(nlk_cb_mutex-GENERIC);
>>[  280.924916]                                lock(dpll_xa_lock);
>>[  280.925454]   lock(nlk_cb_mutex-GENERIC);
>>[  280.925858]
>>                *** DEADLOCK ***
>>
>>[  280.926488] 2 locks held by python3/1058:
>>[  280.926891]  #0: ffffffff827e2430 (cb_lock){++++}-{3:3}, at:
>>genl_rcv+0x15/0x40
>>[  280.927585]  #1: ffffffff827d1c68 (dpll_xa_lock){+.+.}-{3:3}, at:
>>dpll_pin_pre_dumpit+0x13/0x20
>>[  280.928385]
>>               stack backtrace:
>>[  280.928853] CPU: 8 PID: 1058 Comm: python3 Tainted: G             L
>>6.3.0jiri+ #4
>>[  280.929586] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
>>rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014
>>[  280.930558] Call Trace:
>>[  280.930849]  <TASK>
>>[  280.931117]  dump_stack_lvl+0x58/0xb0
>>[  280.931500]  check_noncircular+0x11b/0x130
>>[  280.931916]  ? kernel_text_address+0x109/0x110
>>[  280.932353]  __lock_acquire+0x1165/0x26b0
>>[  280.932759]  lock_acquire+0xce/0x2b0
>>[  280.933130]  ? netlink_dump+0x4a/0x400
>>[  280.933517]  __mutex_lock+0x91/0xbb0
>>[  280.933885]  ? netlink_dump+0x4a/0x400
>>[  280.934269]  ? netlink_dump+0x4a/0x400
>>[  280.934662]  ? netlink_dump+0x4a/0x400
>>[  280.935054]  netlink_dump+0x4a/0x400
>>[  280.935426]  __netlink_dump_start+0x188/0x230
>>[  280.935857]  genl_family_rcv_msg_dumpit+0xf9/0x110
>>[  280.936321]  ? genl_family_rcv_msg_attrs_parse.constprop.0+0xe0/0xe0
>>[  280.936887]  ? dpll_nl_pin_get_doit+0x100/0x100
>>[  280.937324]  ? genl_lock_dumpit+0x50/0x50
>>[  280.937729]  genl_rcv_msg+0x115/0x290
>>[  280.938109]  ? dpll_pin_post_doit+0x20/0x20
>>[  280.938526]  ? dpll_nl_pin_get_doit+0x100/0x100
>>[  280.938966]  ? dpll_pin_pre_dumpit+0x20/0x20
>>[  280.939390]  ? genl_family_rcv_msg_doit.isra.0+0x110/0x110
>>[  280.939904]  netlink_rcv_skb+0x54/0x100
>>[  280.940296]  genl_rcv+0x24/0x40
>>[  280.940636]  netlink_unicast+0x182/0x260
>>[  280.941034]  netlink_sendmsg+0x242/0x4b0
>>[  280.941439]  sock_sendmsg+0x38/0x60
>>[  280.941804]  ? sockfd_lookup_light+0x12/0x70
>>[  280.942230]  __sys_sendto+0xeb/0x130
>>[  280.942616]  ? mntput_no_expire+0x7e/0x490
>>[  280.943038]  ? proc_nr_files+0x30/0x30
>>[  280.943425]  __x64_sys_sendto+0x20/0x30
>>[  280.943817]  do_syscall_64+0x3c/0x80
>>[  280.944194]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
>>[  280.944674] RIP: 0033:0x7f252fd132b0
>>[  280.945042] Code: c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64
>>8b 04 25 18 00 00 00 85 c0 75 1d 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05
>><48> 3d 00 f0 ff ff 77 68 c3 0f 1f 80 00 00 00 00 41 54 48 83 ec 20
>>[  280.946622] RSP: 002b:00007ffdbd9335d8 EFLAGS: 00000246 ORIG_RAX:
>>000000000000002c
>>[  280.947328] RAX: ffffffffffffffda RBX: 00007ffdbd933688 RCX:
>>00007f252fd132b0
>>[  280.947962] RDX: 0000000000000014 RSI: 00007f252ede65d0 RDI:
>>0000000000000003
>>[  280.948594] RBP: 00007f252f806da0 R08: 0000000000000000 R09:
>>0000000000000000
>>[  280.949229] R10: 0000000000000000 R11: 0000000000000246 R12:
>>0000000000000000
>>[  280.949858] R13: ffffffffc4653600 R14: 0000000000000001 R15:
>>00007f252f74d147
>>[  280.950494]  </TASK>
>>
>>Problem is that in __netlink_dump_start() you take dpll_xa_lock
>>(in control->start(cb)) while holding nlk->cb_mutex, then you unlock
>>the nlk->cb_mutex and take it again in netlink_dump().
>>I hear "Chiquitita" from the distance :)
>>
>>[...]
>
>Well I tested it, but haven't seen such outcome, do you have any script
>for reproducing this behavior?

Any dump will do. For example:
sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump device-get

This is still present in RFCv8.



>
>Thank you,
>Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 149+ messages in thread

end of thread, other threads:[~2023-06-13 13:50 UTC | newest]

Thread overview: 149+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-28  0:20 [RFC PATCH v7 0/8] Create common DPLL configuration API Vadim Fedorenko
2023-04-28  0:20 ` Vadim Fedorenko
2023-04-28  0:20 ` [RFC PATCH v7 1/8] dpll: spec: Add Netlink spec in YAML Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-05-04 12:02   ` Jiri Pirko
2023-05-04 12:02     ` Jiri Pirko
2023-05-04 21:24     ` Jakub Kicinski
2023-05-04 21:24       ` Jakub Kicinski
2023-05-05 10:29       ` Jiri Pirko
2023-05-05 10:29         ` Jiri Pirko
2023-05-11  7:44         ` Kubalewski, Arkadiusz
2023-05-11  8:00           ` Jiri Pirko
2023-05-11 14:55             ` Jakub Kicinski
2023-05-11  7:40       ` Kubalewski, Arkadiusz
2023-05-11  7:59         ` Jiri Pirko
2023-05-11 20:51           ` Kubalewski, Arkadiusz
2023-05-15  9:30             ` Jiri Pirko
2023-05-15  9:30               ` Jiri Pirko
2023-05-16 12:05               ` Kubalewski, Arkadiusz
2023-05-16 12:05                 ` Kubalewski, Arkadiusz
2023-05-16 14:33                 ` Jiri Pirko
2023-05-16 14:33                   ` Jiri Pirko
2023-05-18 13:24                   ` Kubalewski, Arkadiusz
2023-05-18 13:24                     ` Kubalewski, Arkadiusz
2023-05-18 14:02                     ` Jiri Pirko
2023-05-18 14:02                       ` Jiri Pirko
2023-05-11 15:20         ` Jakub Kicinski
2023-05-11 20:53           ` Kubalewski, Arkadiusz
2023-05-11 23:29             ` Jakub Kicinski
2023-05-16 12:15               ` Kubalewski, Arkadiusz
2023-05-16 12:15                 ` Kubalewski, Arkadiusz
2023-05-11  7:38     ` Kubalewski, Arkadiusz
2023-05-11  8:14       ` Jiri Pirko
2023-05-11 20:53         ` Kubalewski, Arkadiusz
2023-05-11 15:26       ` Jakub Kicinski
2023-05-11 20:54         ` Kubalewski, Arkadiusz
2023-04-28  0:20 ` [RFC PATCH v7 2/8] dpll: Add DPLL framework base functions Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-05-02 15:38   ` Jiri Pirko
2023-05-02 15:38     ` Jiri Pirko
2023-06-06 18:47     ` Kubalewski, Arkadiusz
2023-06-06 18:47       ` Kubalewski, Arkadiusz
2023-05-03  8:09   ` Jiri Pirko
2023-05-03  8:09     ` Jiri Pirko
2023-06-06 18:50     ` Kubalewski, Arkadiusz
2023-06-06 18:50       ` Kubalewski, Arkadiusz
2023-05-04 11:18   ` Jiri Pirko
2023-05-04 11:18     ` Jiri Pirko
2023-05-04 20:27     ` Jakub Kicinski
2023-05-04 20:27       ` Jakub Kicinski
2023-06-06 18:55       ` Kubalewski, Arkadiusz
2023-06-06 18:55         ` Kubalewski, Arkadiusz
2023-05-04 21:21   ` Jakub Kicinski
2023-05-04 21:21     ` Jakub Kicinski
2023-06-09 12:54     ` Kubalewski, Arkadiusz
2023-06-09 12:54       ` Kubalewski, Arkadiusz
2023-05-09 13:40   ` Jiri Pirko
2023-06-09 12:53     ` Kubalewski, Arkadiusz
2023-06-09 12:53       ` Kubalewski, Arkadiusz
2023-06-13 13:49       ` Jiri Pirko
2023-06-13 13:49         ` Jiri Pirko
2023-05-22 14:45   ` Paolo Abeni
2023-05-22 14:45     ` Paolo Abeni
2023-06-09 12:51     ` Kubalewski, Arkadiusz
2023-06-09 12:51       ` Kubalewski, Arkadiusz
2023-04-28  0:20 ` [RFC PATCH v7 3/8] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-05-02  7:50   ` kernel test robot
2023-05-04 19:04   ` Jakub Kicinski
2023-05-04 19:04     ` Jakub Kicinski
2023-05-05 13:16     ` Vadim Fedorenko
2023-05-05 13:16       ` Vadim Fedorenko
2023-05-05 15:29       ` Jakub Kicinski
2023-05-05 15:29         ` Jakub Kicinski
2023-05-11 10:23         ` Kubalewski, Arkadiusz
2023-04-28  0:20 ` [RFC PATCH v7 4/8] ice: add admin commands to access cgu configuration Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-04-28  3:20   ` kernel test robot
2023-04-28 17:53   ` kernel test robot
2023-04-28  0:20 ` [RFC PATCH v7 5/8] ice: implement dpll interface to control cgu Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-04-28  6:05   ` kernel test robot
2023-04-28  8:18   ` kernel test robot
2023-05-03 12:18   ` Jiri Pirko
2023-05-03 12:18     ` Jiri Pirko
2023-05-15 22:07     ` Kubalewski, Arkadiusz
2023-05-15 22:07       ` Kubalewski, Arkadiusz
2023-05-16  6:26       ` Jiri Pirko
2023-05-16  6:26         ` Jiri Pirko
2023-05-18 16:06         ` Kubalewski, Arkadiusz
2023-05-18 16:06           ` Kubalewski, Arkadiusz
2023-05-19  6:15           ` Jiri Pirko
2023-05-19  6:15             ` Jiri Pirko
2023-05-25  9:01             ` Kubalewski, Arkadiusz
2023-05-25  9:01               ` Kubalewski, Arkadiusz
2023-05-19  6:47         ` Paolo Abeni
2023-05-19  6:47           ` Paolo Abeni
2023-05-25  9:05           ` Kubalewski, Arkadiusz
2023-05-25  9:05             ` Kubalewski, Arkadiusz
2023-05-15 17:12   ` Jiri Pirko
2023-05-15 17:12     ` Jiri Pirko
2023-05-16  9:22     ` Kubalewski, Arkadiusz
2023-05-16  9:22       ` Kubalewski, Arkadiusz
2023-05-16 11:46       ` Jiri Pirko
2023-05-16 11:46         ` Jiri Pirko
2023-05-18 16:07         ` Kubalewski, Arkadiusz
2023-05-18 16:07           ` Kubalewski, Arkadiusz
2023-05-19  6:15           ` Jiri Pirko
2023-05-19  6:15             ` Jiri Pirko
2023-04-28  0:20 ` [RFC PATCH v7 6/8] ptp_ocp: implement DPLL ops Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-04-28  6:36   ` kernel test robot
2023-05-04  9:27   ` Jiri Pirko
2023-05-04  9:27     ` Jiri Pirko
2023-05-05 13:43     ` Vadim Fedorenko
2023-05-05 13:43       ` Vadim Fedorenko
2023-05-06 12:42       ` Jiri Pirko
2023-05-06 12:42         ` Jiri Pirko
2023-04-28  0:20 ` [RFC PATCH v7 7/8] netdev: expose DPLL pin handle for netdevice Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-04-28  2:36   ` Stephen Hemminger
2023-04-28  2:36     ` Stephen Hemminger
2023-04-28 10:00     ` Vadim Fedorenko
2023-04-28 10:00       ` Vadim Fedorenko
2023-04-28  2:49   ` kernel test robot
2023-04-28  3:09   ` kernel test robot
2023-05-04 20:31   ` Jakub Kicinski
2023-05-04 20:31     ` Jakub Kicinski
2023-05-05 10:32     ` Jiri Pirko
2023-05-05 10:32       ` Jiri Pirko
2023-04-28  0:20 ` [RFC PATCH v7 8/8] mlx5: Implement SyncE support using DPLL infrastructure Vadim Fedorenko
2023-04-28  0:20   ` Vadim Fedorenko
2023-05-02  8:55 ` [RFC PATCH v7 0/8] Create common DPLL configuration API Jiri Pirko
2023-05-02  8:55   ` Jiri Pirko
2023-05-02 13:04 ` Jiri Pirko
2023-05-02 13:04   ` Jiri Pirko
2023-05-25 12:52   ` Kubalewski, Arkadiusz
2023-05-25 12:52     ` Kubalewski, Arkadiusz
2023-05-11  7:52 ` Jiri Pirko
2023-05-25 13:01   ` Kubalewski, Arkadiusz
2023-05-25 13:01     ` Kubalewski, Arkadiusz
2023-05-17 10:18 ` Jiri Pirko
2023-05-17 10:18   ` Jiri Pirko
2023-05-26 10:14   ` Kubalewski, Arkadiusz
2023-05-26 10:14     ` Kubalewski, Arkadiusz
2023-05-26 10:39     ` Jiri Pirko
2023-05-26 10:39       ` Jiri Pirko
2023-06-05 10:07       ` Kubalewski, Arkadiusz
2023-06-05 10:07         ` Kubalewski, Arkadiusz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.