All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-12  2:28 ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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.

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

Vadim Fedorenko (3):
  dpll: Add DPLL framework base functions
  dpll: documentation on DPLL subsystem interface
  ptp_ocp: implement DPLL ops

 Documentation/netlink/specs/dpll.yaml         |  514 +++++
 Documentation/networking/dpll.rst             |  347 ++++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    9 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |   10 +
 drivers/dpll/dpll_core.c                      |  835 ++++++++
 drivers/dpll/dpll_core.h                      |   99 +
 drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
 drivers/dpll/dpll_netlink.h                   |   30 +
 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     | 1845 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
 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   |  411 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  206 +-
 include/linux/dpll.h                          |  284 +++
 include/uapi/linux/dpll.h                     |  196 ++
 31 files changed, 7135 insertions(+), 16 deletions(-)
 create mode 100644 Documentation/netlink/specs/dpll.yaml
 create mode 100644 Documentation/networking/dpll.rst
 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 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.34.1


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

* [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-12  2:28 ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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.

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

Vadim Fedorenko (3):
  dpll: Add DPLL framework base functions
  dpll: documentation on DPLL subsystem interface
  ptp_ocp: implement DPLL ops

 Documentation/netlink/specs/dpll.yaml         |  514 +++++
 Documentation/networking/dpll.rst             |  347 ++++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    9 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |   10 +
 drivers/dpll/dpll_core.c                      |  835 ++++++++
 drivers/dpll/dpll_core.h                      |   99 +
 drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
 drivers/dpll/dpll_netlink.h                   |   30 +
 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     | 1845 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
 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   |  411 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  206 +-
 include/linux/dpll.h                          |  284 +++
 include/uapi/linux/dpll.h                     |  196 ++
 31 files changed, 7135 insertions(+), 16 deletions(-)
 create mode 100644 Documentation/netlink/specs/dpll.yaml
 create mode 100644 Documentation/networking/dpll.rst
 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 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] 202+ messages in thread

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

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 | 514 ++++++++++++++++++++++++++
 drivers/dpll/dpll_nl.c                | 126 +++++++
 drivers/dpll/dpll_nl.h                |  42 +++
 include/uapi/linux/dpll.h             | 196 ++++++++++
 4 files changed, 878 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..03e9f0e2d3d2
--- /dev/null
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -0,0 +1,514 @@
+name: dpll
+
+doc: DPLL subsystem.
+
+definitions:
+  -
+    type: const
+    name: temp-divider
+    value: 10
+  -
+    type: const
+    name: pin-freq-1-hz
+    value: 1
+  -
+    type: const
+    name: pin-freq-10-mhz
+    value: 10000000
+  -
+    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 (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: enum
+    name: pin-type
+    doc: Enumerates 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-state
+    doc: available pin modes
+    entries:
+      -
+        name: unspec
+        doc: unspecified value
+      -
+        name: connected
+        doc: pin connected
+      -
+        name: disconnected
+        doc: pin disconnected
+    render-max: true
+  -
+    type: enum
+    name: pin-direction
+    doc: available pin direction
+    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: enum
+    name: mode
+    doc: |
+      working-modes a dpll can support, differentiate if and how dpll selects
+      one of its sources to syntonize with it
+    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: 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: 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
+  -
+    type: flags
+    name: pin-caps
+    doc: define capabilities of a pin
+    entries:
+      -
+        name: direction-can-change
+      -
+        name: priority-can-change
+      -
+        name: state-can-change
+
+
+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: source-pin-idx
+        type: u32
+      -
+        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
+        type: nest
+        multi-attr: true
+        nested-attributes: pin
+        value: 12
+      -
+        name: pin-idx
+        type: u32
+      -
+        name: pin-description
+        type: string
+      -
+        name: pin-type
+        type: u8
+        enum: pin-type
+      -
+        name: pin-direction
+        type: u8
+        enum: pin-direction
+      -
+        name: pin-frequency
+        type: u32
+      -
+        name: pin-frequency-supported
+        type: u32
+        multi-attr: true
+      -
+        name: pin-any-frequency-min
+        type: u32
+      -
+        name: pin-any-frequency-max
+        type: u32
+      -
+        name: pin-prio
+        type: u32
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+      -
+        name: pin-parent
+        type: nest
+        multi-attr: true
+        nested-attributes: pin
+        value: 23
+      -
+        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: source-pin-idx
+        type: u32
+      -
+        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
+        type: nest
+        value: 12
+        multi-attr: true
+        nested-attributes: pin
+      -
+        name: pin-prio
+        type: u32
+        value: 21
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+      -
+        name: pin-dpll-caps
+        type: u32
+        value: 26
+  -
+    name: pin
+    subset-of: dpll
+    attributes:
+      -
+        name: device
+        type: nest
+        value: 1
+        multi-attr: true
+        nested-attributes: device
+      -
+        name: pin-idx
+        type: u32
+        value: 13
+      -
+        name: pin-description
+        type: string
+      -
+        name: pin-type
+        type: u8
+        enum: pin-type
+      -
+        name: pin-direction
+        type: u8
+        enum: pin-direction
+      -
+        name: pin-frequency
+        type: u32
+      -
+        name: pin-frequency-supported
+        type: u32
+        multi-attr: true
+      -
+        name: pin-any-frequency-min
+        type: u32
+      -
+        name: pin-any-frequency-max
+        type: u32
+      -
+        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
+        value: 23
+      -
+        name: pin-rclk-device
+        type: string
+        value: 25
+      -
+        name: pin-dpll-caps
+        type: u32
+  -
+    name: pin-parent
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-state
+        type: u8
+        value: 22
+        enum: pin-state
+      -
+        name: pin-parent-idx
+        type: u32
+        value: 24
+      -
+        name: pin-rclk-device
+        type: string
+
+
+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:
+          attributes:
+            - pin
+
+      dump:
+        pre: dpll-pin-pre-dumpit
+        post: dpll-pin-post-dumpit
+        request:
+          attributes:
+            - id
+            - bus-name
+            - dev-name
+        reply:
+          attributes:
+            - pin
+
+    -
+      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-parent-idx
+            - pin-state
+
+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..099d1e30ca7c
--- /dev/null
+++ b/drivers/dpll/dpll_nl.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: 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_U32, },
+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
+};
+
+/* Ops table for dpll */
+static const struct genl_split_ops dpll_nl_ops[6] = {
+	{
+		.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..3a354dfb9a31
--- /dev/null
+++ b/drivers/dpll/dpll_nl.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: 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..ece6fe685d08
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* 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
+
+#define DPLL_TEMP_DIVIDER	10
+#define DPLL_PIN_FREQ_1_HZ	1
+#define DPLL_PIN_FREQ_10_MHZ	10000000
+
+/**
+ * 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 (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)
+};
+
+/**
+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
+ * @DPLL_PIN_STATE_CONNECTED: pin connected
+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
+ */
+enum dpll_pin_state {
+	DPLL_PIN_STATE_UNSPEC,
+	DPLL_PIN_STATE_CONNECTED,
+	DPLL_PIN_STATE_DISCONNECTED,
+
+	__DPLL_PIN_STATE_MAX,
+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
+};
+
+/**
+ * enum dpll_pin_direction - available pin direction
+ * @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)
+};
+
+/**
+ * enum dpll_mode - working-modes a dpll can support, differentiate if and how
+ *   dpll selects one of its sources to syntonize with it
+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
+ */
+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 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_SOURCE_PIN_IDX,
+	DPLL_A_LOCK_STATUS,
+	DPLL_A_TEMP,
+	DPLL_A_CLOCK_ID,
+	DPLL_A_TYPE,
+	DPLL_A_PIN,
+	DPLL_A_PIN_IDX,
+	DPLL_A_PIN_DESCRIPTION,
+	DPLL_A_PIN_TYPE,
+	DPLL_A_PIN_DIRECTION,
+	DPLL_A_PIN_FREQUENCY,
+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
+	DPLL_A_PIN_ANY_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,
+	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] 202+ messages in thread

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

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 | 514 ++++++++++++++++++++++++++
 drivers/dpll/dpll_nl.c                | 126 +++++++
 drivers/dpll/dpll_nl.h                |  42 +++
 include/uapi/linux/dpll.h             | 196 ++++++++++
 4 files changed, 878 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..03e9f0e2d3d2
--- /dev/null
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -0,0 +1,514 @@
+name: dpll
+
+doc: DPLL subsystem.
+
+definitions:
+  -
+    type: const
+    name: temp-divider
+    value: 10
+  -
+    type: const
+    name: pin-freq-1-hz
+    value: 1
+  -
+    type: const
+    name: pin-freq-10-mhz
+    value: 10000000
+  -
+    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 (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: enum
+    name: pin-type
+    doc: Enumerates 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-state
+    doc: available pin modes
+    entries:
+      -
+        name: unspec
+        doc: unspecified value
+      -
+        name: connected
+        doc: pin connected
+      -
+        name: disconnected
+        doc: pin disconnected
+    render-max: true
+  -
+    type: enum
+    name: pin-direction
+    doc: available pin direction
+    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: enum
+    name: mode
+    doc: |
+      working-modes a dpll can support, differentiate if and how dpll selects
+      one of its sources to syntonize with it
+    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: 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: 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
+  -
+    type: flags
+    name: pin-caps
+    doc: define capabilities of a pin
+    entries:
+      -
+        name: direction-can-change
+      -
+        name: priority-can-change
+      -
+        name: state-can-change
+
+
+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: source-pin-idx
+        type: u32
+      -
+        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
+        type: nest
+        multi-attr: true
+        nested-attributes: pin
+        value: 12
+      -
+        name: pin-idx
+        type: u32
+      -
+        name: pin-description
+        type: string
+      -
+        name: pin-type
+        type: u8
+        enum: pin-type
+      -
+        name: pin-direction
+        type: u8
+        enum: pin-direction
+      -
+        name: pin-frequency
+        type: u32
+      -
+        name: pin-frequency-supported
+        type: u32
+        multi-attr: true
+      -
+        name: pin-any-frequency-min
+        type: u32
+      -
+        name: pin-any-frequency-max
+        type: u32
+      -
+        name: pin-prio
+        type: u32
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+      -
+        name: pin-parent
+        type: nest
+        multi-attr: true
+        nested-attributes: pin
+        value: 23
+      -
+        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: source-pin-idx
+        type: u32
+      -
+        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
+        type: nest
+        value: 12
+        multi-attr: true
+        nested-attributes: pin
+      -
+        name: pin-prio
+        type: u32
+        value: 21
+      -
+        name: pin-state
+        type: u8
+        enum: pin-state
+      -
+        name: pin-dpll-caps
+        type: u32
+        value: 26
+  -
+    name: pin
+    subset-of: dpll
+    attributes:
+      -
+        name: device
+        type: nest
+        value: 1
+        multi-attr: true
+        nested-attributes: device
+      -
+        name: pin-idx
+        type: u32
+        value: 13
+      -
+        name: pin-description
+        type: string
+      -
+        name: pin-type
+        type: u8
+        enum: pin-type
+      -
+        name: pin-direction
+        type: u8
+        enum: pin-direction
+      -
+        name: pin-frequency
+        type: u32
+      -
+        name: pin-frequency-supported
+        type: u32
+        multi-attr: true
+      -
+        name: pin-any-frequency-min
+        type: u32
+      -
+        name: pin-any-frequency-max
+        type: u32
+      -
+        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
+        value: 23
+      -
+        name: pin-rclk-device
+        type: string
+        value: 25
+      -
+        name: pin-dpll-caps
+        type: u32
+  -
+    name: pin-parent
+    subset-of: dpll
+    attributes:
+      -
+        name: pin-state
+        type: u8
+        value: 22
+        enum: pin-state
+      -
+        name: pin-parent-idx
+        type: u32
+        value: 24
+      -
+        name: pin-rclk-device
+        type: string
+
+
+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:
+          attributes:
+            - pin
+
+      dump:
+        pre: dpll-pin-pre-dumpit
+        post: dpll-pin-post-dumpit
+        request:
+          attributes:
+            - id
+            - bus-name
+            - dev-name
+        reply:
+          attributes:
+            - pin
+
+    -
+      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-parent-idx
+            - pin-state
+
+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..099d1e30ca7c
--- /dev/null
+++ b/drivers/dpll/dpll_nl.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: 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_U32, },
+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
+};
+
+/* Ops table for dpll */
+static const struct genl_split_ops dpll_nl_ops[6] = {
+	{
+		.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..3a354dfb9a31
--- /dev/null
+++ b/drivers/dpll/dpll_nl.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: 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..ece6fe685d08
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* 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
+
+#define DPLL_TEMP_DIVIDER	10
+#define DPLL_PIN_FREQ_1_HZ	1
+#define DPLL_PIN_FREQ_10_MHZ	10000000
+
+/**
+ * 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 (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)
+};
+
+/**
+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
+ * @DPLL_PIN_STATE_CONNECTED: pin connected
+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
+ */
+enum dpll_pin_state {
+	DPLL_PIN_STATE_UNSPEC,
+	DPLL_PIN_STATE_CONNECTED,
+	DPLL_PIN_STATE_DISCONNECTED,
+
+	__DPLL_PIN_STATE_MAX,
+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
+};
+
+/**
+ * enum dpll_pin_direction - available pin direction
+ * @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)
+};
+
+/**
+ * enum dpll_mode - working-modes a dpll can support, differentiate if and how
+ *   dpll selects one of its sources to syntonize with it
+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
+ */
+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 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_SOURCE_PIN_IDX,
+	DPLL_A_LOCK_STATUS,
+	DPLL_A_TEMP,
+	DPLL_A_CLOCK_ID,
+	DPLL_A_TYPE,
+	DPLL_A_PIN,
+	DPLL_A_PIN_IDX,
+	DPLL_A_PIN_DESCRIPTION,
+	DPLL_A_PIN_TYPE,
+	DPLL_A_PIN_DIRECTION,
+	DPLL_A_PIN_FREQUENCY,
+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
+	DPLL_A_PIN_ANY_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,
+	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] 202+ messages in thread

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

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.

Changes:
dpll: adapt changes after introduction of dpll yaml spec
dpll: redesign after review comments, fix minor issues
dpll: add get pin command
dpll: _get/_put approach for creating and realesing pin or dpll objects
dpll: lock access to dplls with global lock
dpll: lock access to pins with global lock

dpll: replace cookie with clock id
dpll: 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: follow one naming schema in dpll subsys

dpll: fix dpll device naming scheme

Fix dpll device naming scheme by use of 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.

dpll: remove description length parameter

dpll: fix 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.

dpll: move function comments to dpll_core.c, fix exports
dpll: remove single-use helper functions
dpll: merge device register with alloc
dpll: lock and unlock mutex on dpll device release
dpll: move dpll_type to uapi header
dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
dpll: rename dpll_pin_state to dpll_pin_mode
dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
dpll: remove DPLL_CHANGE_PIN_TYPE enum value

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                 |    9 +
 drivers/Kconfig             |    2 +
 drivers/Makefile            |    1 +
 drivers/dpll/Kconfig        |    7 +
 drivers/dpll/Makefile       |   10 +
 drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    |   99 ++++
 drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   30 +
 include/linux/dpll.h        |  284 ++++++++++
 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)          += dpll_sys.o
+dpll_sys-y                  += dpll_core.o
+dpll_sys-y                  += dpll_netlink.o
+dpll_sys-y                  += dpll_nl.o
+
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..3fc151e16751
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,835 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - Generic DPLL Management class support.
+ *
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#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_device_xa_lock);
+DEFINE_MUTEX(dpll_pin_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))
+
+static struct class dpll_class = {
+	.name = "dpll",
+};
+
+/**
+ * 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)
+{
+	struct dpll_device *dpll = NULL;
+
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		dpll = xa_load(&dpll_device_xa, id);
+
+	return dpll;
+}
+
+/**
+ * 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 index;
+
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
+		    !strcmp(dev_name(&dpll->dev), device_name)) {
+			ret = dpll;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * 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 or increase refcount on existing pin
+ * reference on given xarray.
+ *
+ * Return:
+ * * 0 on success
+ * * -ENOMEM on failed allocation
+ */
+static int
+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
+		    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 idx;
+	int ret;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+	}
+
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->pin = pin;
+	ref->ops = ops;
+	ref->priv = priv;
+	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		refcount_set(&ref->refcount, 1);
+	else
+		kfree(ref);
+
+	return ret;
+}
+
+/**
+ * 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 references are dropped, 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)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin) {
+			if (refcount_dec_and_test(&ref->refcount)) {
+				xa_erase(xa_pins, i);
+				kfree(ref);
+			}
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * dpll_xa_ref_pin_find - find pin reference on xarray
+ * @xa_pins: dpll_pin_ref xarray holding pins
+ * @pin: pointer to a pin
+ *
+ * Search for pin reference struct of a given pin 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_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin)
+			return ref;
+	}
+
+	return NULL;
+}
+
+/**
+ * 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,
+		     struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 idx;
+	int ret;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll == dpll) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+	}
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->dpll = dpll;
+	ref->ops = ops;
+	ref->priv = priv;
+	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		refcount_set(&ref->refcount, 1);
+	else
+		kfree(ref);
+
+	return ret;
+}
+
+/**
+ * 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)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll == dpll) {
+			if (refcount_dec_and_test(&ref->refcount)) {
+				xa_erase(xa_dplls, i);
+				kfree(ref);
+			}
+			break;
+		}
+	}
+}
+
+/**
+ * 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;
+}
+
+
+/**
+ * dpll_device_alloc - allocate the memory for dpll device
+ * @clock_id: clock_id of creator
+ * @dev_driver_id: 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
+ */
+struct dpll_device *
+dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, 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);
+	dpll->dev.class = &dpll_class;
+	dpll->dev_driver_id = dev_driver_id;
+	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
+ * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each(&dpll_device_xa, index, dpll) {
+		if (dpll->clock_id == clock_id &&
+		    dpll->dev_driver_id == dev_driver_id &&
+		    dpll->module == module) {
+			ret = dpll;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
+	mutex_unlock(&dpll_device_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_device_xa_lock);
+	if (refcount_dec_and_test(&dpll->refcount)) {
+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+		xa_destroy(&dpll->pin_refs);
+		xa_erase(&dpll_device_xa, dpll->id);
+		kfree(dpll);
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_put);
+
+/**
+ * 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,
+			 struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
+{
+	if (WARN_ON(!ops || !owner))
+		return -EINVAL;
+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+	mutex_lock(&dpll_device_xa_lock);
+	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return -EEXIST;
+	}
+	dpll->dev.bus = owner->bus;
+	dpll->parent = owner;
+	dpll->type = type;
+	dpll->ops = ops;
+	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
+		     dpll->dev_driver_id);
+	dpll->priv = priv;
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_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
+ *
+ * Deregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll_device_xa_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_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
+ * @dev_driver_id: id 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
+ */
+struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	pin->dev_driver_id = device_drv_id;
+	pin->clock_id = clock_id;
+	pin->module = module;
+	refcount_set(&pin->refcount, 1);
+	if (WARN_ON(!prop->description)) {
+		ret = -EINVAL;
+		goto release;
+	}
+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
+	if (!pin->prop.description) {
+		ret = -ENOMEM;
+		goto release;
+	}
+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto release;
+	}
+	pin->prop.type = prop->type;
+	pin->prop.capabilities = prop->capabilities;
+	pin->prop.freq_supported = prop->freq_supported;
+	pin->prop.any_freq_min = prop->any_freq_min;
+	pin->prop.any_freq_max = prop->any_freq_max;
+	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->idx, pin,
+		       xa_limit_16b, GFP_KERNEL);
+release:
+	if (ret) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		kfree(pin->prop.description);
+		kfree(pin->rclk_dev_name);
+		kfree(pin);
+		return ERR_PTR(ret);
+	}
+
+	return pin;
+}
+
+/**
+ * dpll_pin_get - find existing or create new dpll pin
+ * @clock_id: clock_id of creator
+ * @dev_driver_id: id 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 device_drv_id, struct module *module,
+	     const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pos, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_pin_xa_lock);
+	xa_for_each(&dpll_pin_xa, index, pos) {
+		if (pos->clock_id == clock_id &&
+		    pos->dev_driver_id == device_drv_id &&
+		    pos->module == module) {
+			ret = pos;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	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;
+	mutex_lock(&dpll_pin_xa_lock);
+	if (refcount_dec_and_test(&pin->refcount)) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		xa_erase(&dpll_pin_xa, pin->idx);
+		kfree(pin->prop.description);
+		kfree(pin->rclk_dev_name);
+		kfree(pin);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_put);
+
+static int
+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		    struct dpll_pin_ops *ops, void *priv,
+		    const char *rclk_device_name)
+{
+	int ret;
+
+	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);
+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,
+		  struct dpll_pin_ops *ops, void *priv,
+		  struct device *rclk_device)
+{
+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
+	int ret;
+
+	if (WARN_ON(!dpll))
+		return -EINVAL;
+	if (WARN_ON(!pin))
+		return -EINVAL;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+static void
+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
+}
+
+/**
+ * dpll_pin_unregister - deregister dpll pin from dpll device
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ *
+ * Note: It does not free the memory
+ */
+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
+		return -ENOENT;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	__dpll_pin_unregister(dpll, pin);
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return 0;
+}
+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,
+			 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(!pin || !parent))
+		return -EINVAL;
+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
+		return -EPERM;
+	mutex_lock(&dpll_pin_xa_lock);
+	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_device_xa_lock);
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
+					  rclk_device ?
+					  dev_name(rclk_device) : NULL);
+		mutex_unlock(&dpll_device_xa_lock);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref) {
+		if (i < stop) {
+			mutex_lock(&dpll_device_xa_lock);
+			__dpll_pin_unregister(ref->dpll, pin);
+			mutex_unlock(&dpll_device_xa_lock);
+		}
+	}
+	refcount_dec(&pin->refcount);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+unlock:
+	mutex_unlock(&dpll_pin_xa_lock);
+	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
+ *
+ * Note: It does not free the memory
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	refcount_dec(&pin->refcount);
+	xa_for_each(&pin->dpll_refs, i, ref) {
+		__dpll_pin_unregister(ref->dpll, pin);
+		dpll_pin_parent_notify(ref->dpll, pin, parent,
+				       DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+/**
+ * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
+ * @dpll: dpll device pointer
+ * @idx: index of pin
+ *
+ * Find a reference to a pin registered with given dpll and return its pointer.
+ *
+ * Return:
+ * * valid pointer if pin was found
+ * * NULL if not found
+ */
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
+{
+	struct dpll_pin_ref *pos;
+	unsigned long i;
+
+	xa_for_each(&dpll->pin_refs, i, pos) {
+		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
+			return pos->pin;
+	}
+
+	return NULL;
+}
+
+/**
+ * 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)
+{
+	return dpll->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_priv);
+
+/**
+ * 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_ref *ref;
+
+	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
+
+	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	ret = class_register(&dpll_class);
+	if (ret)
+		goto unregister_netlink;
+
+	return 0;
+
+unregister_netlink:
+	dpll_netlink_finish();
+error:
+	mutex_destroy(&dpll_device_xa_lock);
+	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,99 @@
+/* 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/refcount.h>
+#include "dpll_netlink.h"
+
+#define DPLL_REGISTERED		XA_MARK_1
+
+/**
+ * struct dpll_device - structure for a DPLL device
+ * @id:			unique id number for each device
+ * @dev_driver_id:	id given by dev driver
+ * @dev:		struct device for this dpll device
+ * @parent:		parent device
+ * @module:		module of creator
+ * @ops:		operations this &dpll_device supports
+ * @lock:		mutex to serialize operations
+ * @type:		type of a dpll
+ * @priv:		pointer to private information of owner
+ * @pins:		list of pointers to pins registered with this dpll
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @mode_supported_mask: mask of supported modes
+ * @refcount:		refcount
+ **/
+struct dpll_device {
+	u32 id;
+	u32 dev_driver_id;
+	struct device dev;
+	struct device *parent;
+	struct module *module;
+	struct dpll_device_ops *ops;
+	enum dpll_type type;
+	void *priv;
+	struct xarray pin_refs;
+	u64 clock_id;
+	unsigned long mode_supported_mask;
+	refcount_t refcount;
+};
+
+/**
+ * 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 idx;
+	u32 dev_driver_id;
+	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_ref - structure for referencing either dpll or pins
+ * @dpll:		pointer to a dpll
+ * @pin:		pointer to a pin
+ * @ops:		ops for a dpll pin
+ * @priv:		pointer to private information of owner
+ **/
+struct dpll_pin_ref {
+	union {
+		struct dpll_device *dpll;
+		struct dpll_pin *pin;
+	};
+	struct dpll_pin_ops *ops;
+	void *priv;
+	refcount_t refcount;
+};
+
+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);
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
+struct dpll_pin_ref *
+dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
+struct dpll_pin_ref *
+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
+extern struct xarray dpll_device_xa;
+extern struct xarray dpll_pin_xa;
+extern struct mutex dpll_device_xa_lock;
+extern struct mutex dpll_pin_xa_lock;
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..46aefeb1ac93
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,1065 @@
+// 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>
+
+static u32 dpll_pin_freq_value[] = {
+	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
+	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
+};
+
+static int
+dpll_msg_add_dev_handle(struct sk_buff *msg, const 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, const struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	enum dpll_mode mode;
+
+	if (WARN_ON(!dpll->ops->mode_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->mode_get(dpll, &mode, extack))
+		return -EFAULT;
+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
+			    struct netlink_ext_ack *extack)
+{
+	u32 source_pin_idx;
+
+	if (WARN_ON(!dpll->ops->source_pin_idx_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
+		return -EFAULT;
+	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	enum dpll_lock_status status;
+
+	if (WARN_ON(!dpll->ops->lock_status_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->lock_status_get(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)
+{
+	s32 temp;
+
+	if (!dpll->ops->temp_get)
+		return -EOPNOTSUPP;
+	if (dpll->ops->temp_get(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)
+{
+	u32 prio;
+
+	if (!ref->ops->prio_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->prio_get(pin, ref->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)
+{
+	enum dpll_pin_state state;
+
+	if (!ref->ops->state_on_dpll_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_dpll_get(pin, ref->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 netlink_ext_ack *extack)
+{
+	enum dpll_pin_direction direction;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
+		if (ref && ref->ops && ref->dpll)
+			break;
+	}
+	if (!ref || !ref->ops || !ref->dpll)
+		return -ENODEV;
+	if (!ref->ops->direction_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
+{
+	enum dpll_pin_freq_supp fs;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 freq;
+
+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
+		if (ref && ref->ops && ref->dpll)
+			break;
+	}
+	if (!ref || !ref->ops || !ref->dpll)
+		return -ENODEV;
+	if (!ref->ops->frequency_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
+		return -EFAULT;
+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
+		return -EMSGSIZE;
+	if (!dump_any_freq)
+		return 0;
+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
+		if (test_bit(fs, &pin->prop.freq_supported)) {
+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
+			    dpll_pin_freq_value[fs]))
+				return -EMSGSIZE;
+		}
+	}
+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
+				pin->prop.any_freq_min))
+			return -EMSGSIZE;
+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
+				pin->prop.any_freq_max))
+			return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref_parent;
+	enum dpll_pin_state state;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref_parent) {
+		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
+			return -EFAULT;
+		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
+							&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,
+				ref_parent->pin->dev_driver_id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref = NULL;
+	enum dpll_pin_state state;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref) {
+		if (WARN_ON(!ref->ops->state_on_pin_get))
+			return -EFAULT;
+		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
+				ref->pin->dev_driver_id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
+		       struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	struct nlattr *attr;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->dpll_refs, index, ref) {
+		attr = nla_nest_start(msg, DPLL_A_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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
+		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, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
+	if (ret)
+		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, bool dump_dpll)
+{
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
+	if (ret)
+		return ret;
+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
+		if (ret)
+			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_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
+		     struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	enum dpll_mode mode;
+	unsigned long i;
+	int ret;
+
+	ret = dpll_msg_add_dev_handle(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_source_pin_idx(msg, dpll, extack);
+	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;
+	xa_for_each(&dpll->pin_refs, i, ref) {
+		struct nlattr *nest = nla_nest_start(msg, DPLL_A_PIN);
+
+		if (!nest) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_cmd_pin_on_dpll_get(msg, ref->pin, dpll, extack);
+		if (ret) {
+			nla_nest_cancel(msg, nest);
+			break;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return ret;
+}
+
+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
+{
+	enum dpll_pin_freq_supp fs;
+
+	if (freq >= pin->prop.any_freq_min && freq <= pin->prop.any_freq_max)
+		return true;
+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++)
+		if (test_bit(fs, &pin->prop.freq_supported))
+			if (freq == dpll_pin_freq_value[fs])
+				return true;
+	return false;
+}
+
+static int
+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
+		  struct netlink_ext_ack *extack)
+{
+	u32 freq = nla_get_u32(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) {
+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
+		if (ret)
+			return -EFAULT;
+		dpll_pin_notify(ref->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)
+{
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *parent;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	parent = dpll_pin_get_by_idx(dpll, parent_idx);
+	if (!parent)
+		return -EINVAL;
+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
+	if (!ref)
+		return -EINVAL;
+	if (!ref->ops || !ref->ops->state_on_pin_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
+		return -EFAULT;
+	dpll_pin_parent_notify(dpll, pin, parent, 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)
+{
+	struct dpll_pin_ref *ref;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	if (!ref->ops || !ref->ops->state_on_dpll_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
+		return -EINVAL;
+	dpll_pin_notify(ref->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)
+{
+	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 = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	if (!ref->ops || !ref->ops->prio_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->prio_set(pin, 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) {
+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
+			return -EFAULT;
+		dpll_pin_notify(ref->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;
+	u32 parent_idx = PIN_IDX_INVALID;
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLL_A_PIN_FREQUENCY:
+			ret = dpll_pin_freq_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_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_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_idx == PIN_IDX_INVALID) {
+			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 nlattr *hdr, *nest;
+	struct sk_buff *msg;
+	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;
+	nest = nla_nest_start(msg, DPLL_A_PIN);
+	if (!nest)
+		return -EMSGSIZE;
+	ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack, true);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	nla_nest_end(msg, nest);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct nlattr *hdr, *nest;
+	struct dpll_pin *pin;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_nl_family, 0, DPLL_CMD_PIN_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	xa_for_each(&dpll_pin_xa, i, pin) {
+		if (xa_empty(&pin->dpll_refs))
+			continue;
+		nest = nla_nest_start(skb, DPLL_A_PIN);
+		if (!nest) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack, true);
+		if (ret) {
+			nla_nest_cancel(skb, nest);
+			break;
+		}
+		nla_nest_end(skb, nest);
+	}
+
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	return ret;
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	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);
+
+			if (!dpll->ops || !dpll->ops->mode_set)
+				return -EOPNOTSUPP;
+			ret = dpll->ops->mode_set(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 nlattr *hdr, *nest;
+	struct sk_buff *msg;
+	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;
+
+	nest = nla_nest_start(msg, DPLL_A_DEVICE);
+	ret = dpll_device_get_one(dpll, msg, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	nla_nest_end(msg, nest);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct nlattr *hdr, *nest;
+	struct dpll_device *dpll;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
+		ret = dpll_msg_add_dev_handle(skb, dpll);
+		if (ret) {
+			nla_nest_cancel(skb, nest);
+			break;
+		}
+		nla_nest_end(skb, nest);
+	}
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	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, *dpll_name = NULL;
+	int ret = -ENODEV;
+
+	if (!info->attrs[DPLL_A_ID] &&
+	    !(info->attrs[DPLL_A_BUS_NAME] && info->attrs[DPLL_A_DEV_NAME]))
+		return -EINVAL;
+
+	mutex_lock(&dpll_device_xa_lock);
+	if (info->attrs[DPLL_A_ID]) {
+		u32 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;
+	}
+	if (info->attrs[DPLL_A_BUS_NAME] &&
+	    info->attrs[DPLL_A_DEV_NAME]) {
+		const char *bus_name = nla_data(info->attrs[DPLL_A_BUS_NAME]);
+		const char *dev_name = nla_data(info->attrs[DPLL_A_DEV_NAME]);
+
+		dpll_name = dpll_device_get_by_name(bus_name, dev_name);
+		if (!dpll_name) {
+			ret = -ENODEV;
+			goto unlock;
+		}
+
+		if (dpll_id && dpll_name != dpll_id)
+			goto unlock;
+		info->user_ptr[0] = dpll_name;
+	}
+
+	return 0;
+unlock:
+	mutex_unlock(&dpll_device_xa_lock);
+	return ret;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_device_xa_lock);
+}
+
+int dpll_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_device_xa_lock);
+
+	return 0;
+}
+
+int dpll_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_device_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_device *dpll;
+	struct dpll_pin *pin;
+
+	if (ret)
+		return ret;
+	dpll = info->user_ptr[0];
+	if (!info->attrs[DPLL_A_PIN_IDX]) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	mutex_lock(&dpll_pin_xa_lock);
+	pin = dpll_pin_get_by_idx(dpll,
+				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
+	if (!pin) {
+		ret = -ENODEV;
+		goto unlock_pin;
+	}
+	info->user_ptr[1] = pin;
+
+	return 0;
+
+unlock_pin:
+	mutex_unlock(&dpll_pin_xa_lock);
+unlock_dev:
+	mutex_unlock(&dpll_device_xa_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	mutex_unlock(&dpll_pin_xa_lock);
+	dpll_post_doit(ops, skb, info);
+}
+
+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_pin_xa_lock);
+
+	return dpll_pre_dumpit(cb);
+}
+
+int dpll_pin_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	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->dev_driver_id))
+		return -EMSGSIZE;
+
+	switch (attr) {
+	case DPLL_A_MODE:
+		ret = dpll_msg_add_mode(msg, dpll, NULL);
+		break;
+	case DPLL_A_SOURCE_PIN_IDX:
+		ret = dpll_msg_add_source_pin_idx(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:
+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
+		break;
+	case DPLL_A_PIN_PRIO:
+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
+		break;
+	case DPLL_A_PIN_STATE:
+		if (parent) {
+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
+			if (!ref)
+				return -EFAULT;
+			if (!ref->ops || !ref->ops->state_on_pin_get)
+				return -EOPNOTSUPP;
+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
+							 NULL);
+			if (ret)
+				return ret;
+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
+					parent->dev_driver_id))
+				return -EMSGSIZE;
+		} else {
+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+			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);
+}
+
+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..072efa10f0e6
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,30 @@
+/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr);
+
+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..db98b6d4bb73
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. 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;
+
+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
+			struct netlink_ext_ack *extack);
+	int (*mode_set)(const struct dpll_device *dpll,
+			const enum dpll_mode mode,
+			struct netlink_ext_ack *extack);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+			       const enum dpll_mode mode,
+			       struct netlink_ext_ack *extack);
+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
+				  u32 *pin_idx,
+				  struct netlink_ext_ack *extack);
+	int (*lock_status_get)(const struct dpll_device *dpll,
+			       enum dpll_lock_status *status,
+			       struct netlink_ext_ack *extack);
+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
+			struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_ops {
+	int (*frequency_set)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     const u32 frequency,
+			     struct netlink_ext_ack *extack);
+	int (*frequency_get)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     u32 *frequency, struct netlink_ext_ack *extack);
+	int (*direction_set)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     const enum dpll_pin_direction direction,
+			     struct netlink_ext_ack *extack);
+	int (*direction_get)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     enum dpll_pin_direction *direction,
+			     struct netlink_ext_ack *extack);
+	int (*state_on_pin_get)(const struct dpll_pin *pin,
+				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,
+				 const struct dpll_device *dpll,
+				 enum dpll_pin_state *state,
+				 struct netlink_ext_ack *extack);
+	int (*state_on_pin_set)(const struct dpll_pin *pin,
+				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,
+				 const struct dpll_device *dpll,
+				 const enum dpll_pin_state state,
+				 struct netlink_ext_ack *extack);
+	int (*prio_get)(const struct dpll_pin *pin,
+			const struct dpll_device *dpll,
+			u32 *prio, struct netlink_ext_ack *extack);
+	int (*prio_set)(const struct dpll_pin *pin,
+			const struct dpll_device *dpll,
+			const u32 prio, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_properties {
+	const char *description;
+	enum dpll_pin_type type;
+	unsigned long freq_supported;
+	u32 any_freq_min;
+	u32 any_freq_max;
+	unsigned long capabilities;
+};
+
+enum dpll_pin_freq_supp {
+	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
+	DPLL_PIN_FREQ_SUPP_1_HZ,
+	DPLL_PIN_FREQ_SUPP_10_MHZ,
+
+	__DPLL_PIN_FREQ_SUPP_MAX,
+	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
+};
+
+/**
+ * dpll_device_get - find or create dpll_device object
+ * @clock_id: a system unique number for a device
+ * @dev_driver_idx: 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,
+			 struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
+
+/**
+ * dpll_device_unregister - deregister registered dpll
+ * @dpll: pointer to dpll
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll);
+
+/**
+ * dpll_priv - get dpll private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private data pointer passed to dpll subsystem when allocating
+ * device with ``dpll_device_alloc(..)``
+ */
+void *dpll_priv(const struct dpll_device *dpll);
+
+/**
+ * dpll_pin_on_pin_priv - get pin on pin pair private data
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a dpll_pin
+ *
+ * Obtain private pin data pointer passed to dpll subsystem when pin
+ * was registered with parent pin.
+ */
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_on_dpll_priv - get pin on dpll pair private data
+ * @dpll: pointer to dpll
+ * @pin: pointer to a dpll_pin
+ *
+ * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
+ * was registered with a dpll.
+ */
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_get - get reference or create new pin object
+ * @clock_id: a system unique number of a device
+ * @dev_driver_idx: 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_idx 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 *pin_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,
+		      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
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ * Return:
+ * * 0 - pin was successfully deregistered from this dpll device,
+ * * -ENXIO - given pin was not registered with this dpll device,
+ * * -EINVAL - pin pointer is not valid.
+ */
+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * 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,
+			     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);
+
+/**
+ * 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);
+
+
+#endif
-- 
2.34.1


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

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

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.

Changes:
dpll: adapt changes after introduction of dpll yaml spec
dpll: redesign after review comments, fix minor issues
dpll: add get pin command
dpll: _get/_put approach for creating and realesing pin or dpll objects
dpll: lock access to dplls with global lock
dpll: lock access to pins with global lock

dpll: replace cookie with clock id
dpll: 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: follow one naming schema in dpll subsys

dpll: fix dpll device naming scheme

Fix dpll device naming scheme by use of 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.

dpll: remove description length parameter

dpll: fix 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.

dpll: move function comments to dpll_core.c, fix exports
dpll: remove single-use helper functions
dpll: merge device register with alloc
dpll: lock and unlock mutex on dpll device release
dpll: move dpll_type to uapi header
dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
dpll: rename dpll_pin_state to dpll_pin_mode
dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
dpll: remove DPLL_CHANGE_PIN_TYPE enum value

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                 |    9 +
 drivers/Kconfig             |    2 +
 drivers/Makefile            |    1 +
 drivers/dpll/Kconfig        |    7 +
 drivers/dpll/Makefile       |   10 +
 drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    |   99 ++++
 drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   30 +
 include/linux/dpll.h        |  284 ++++++++++
 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)          += dpll_sys.o
+dpll_sys-y                  += dpll_core.o
+dpll_sys-y                  += dpll_netlink.o
+dpll_sys-y                  += dpll_nl.o
+
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..3fc151e16751
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,835 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - Generic DPLL Management class support.
+ *
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#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_device_xa_lock);
+DEFINE_MUTEX(dpll_pin_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))
+
+static struct class dpll_class = {
+	.name = "dpll",
+};
+
+/**
+ * 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)
+{
+	struct dpll_device *dpll = NULL;
+
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		dpll = xa_load(&dpll_device_xa, id);
+
+	return dpll;
+}
+
+/**
+ * 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 index;
+
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
+		    !strcmp(dev_name(&dpll->dev), device_name)) {
+			ret = dpll;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * 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 or increase refcount on existing pin
+ * reference on given xarray.
+ *
+ * Return:
+ * * 0 on success
+ * * -ENOMEM on failed allocation
+ */
+static int
+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
+		    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 idx;
+	int ret;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+	}
+
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->pin = pin;
+	ref->ops = ops;
+	ref->priv = priv;
+	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		refcount_set(&ref->refcount, 1);
+	else
+		kfree(ref);
+
+	return ret;
+}
+
+/**
+ * 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 references are dropped, 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)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin) {
+			if (refcount_dec_and_test(&ref->refcount)) {
+				xa_erase(xa_pins, i);
+				kfree(ref);
+			}
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * dpll_xa_ref_pin_find - find pin reference on xarray
+ * @xa_pins: dpll_pin_ref xarray holding pins
+ * @pin: pointer to a pin
+ *
+ * Search for pin reference struct of a given pin 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_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin == pin)
+			return ref;
+	}
+
+	return NULL;
+}
+
+/**
+ * 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,
+		     struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 idx;
+	int ret;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll == dpll) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+	}
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->dpll = dpll;
+	ref->ops = ops;
+	ref->priv = priv;
+	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		refcount_set(&ref->refcount, 1);
+	else
+		kfree(ref);
+
+	return ret;
+}
+
+/**
+ * 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)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll == dpll) {
+			if (refcount_dec_and_test(&ref->refcount)) {
+				xa_erase(xa_dplls, i);
+				kfree(ref);
+			}
+			break;
+		}
+	}
+}
+
+/**
+ * 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;
+}
+
+
+/**
+ * dpll_device_alloc - allocate the memory for dpll device
+ * @clock_id: clock_id of creator
+ * @dev_driver_id: 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
+ */
+struct dpll_device *
+dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, 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);
+	dpll->dev.class = &dpll_class;
+	dpll->dev_driver_id = dev_driver_id;
+	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
+ * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each(&dpll_device_xa, index, dpll) {
+		if (dpll->clock_id == clock_id &&
+		    dpll->dev_driver_id == dev_driver_id &&
+		    dpll->module == module) {
+			ret = dpll;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
+	mutex_unlock(&dpll_device_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_device_xa_lock);
+	if (refcount_dec_and_test(&dpll->refcount)) {
+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+		xa_destroy(&dpll->pin_refs);
+		xa_erase(&dpll_device_xa, dpll->id);
+		kfree(dpll);
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_put);
+
+/**
+ * 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,
+			 struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
+{
+	if (WARN_ON(!ops || !owner))
+		return -EINVAL;
+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+	mutex_lock(&dpll_device_xa_lock);
+	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return -EEXIST;
+	}
+	dpll->dev.bus = owner->bus;
+	dpll->parent = owner;
+	dpll->type = type;
+	dpll->ops = ops;
+	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
+		     dpll->dev_driver_id);
+	dpll->priv = priv;
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_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
+ *
+ * Deregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll_device_xa_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_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
+ * @dev_driver_id: id 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
+ */
+struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	pin->dev_driver_id = device_drv_id;
+	pin->clock_id = clock_id;
+	pin->module = module;
+	refcount_set(&pin->refcount, 1);
+	if (WARN_ON(!prop->description)) {
+		ret = -EINVAL;
+		goto release;
+	}
+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
+	if (!pin->prop.description) {
+		ret = -ENOMEM;
+		goto release;
+	}
+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto release;
+	}
+	pin->prop.type = prop->type;
+	pin->prop.capabilities = prop->capabilities;
+	pin->prop.freq_supported = prop->freq_supported;
+	pin->prop.any_freq_min = prop->any_freq_min;
+	pin->prop.any_freq_max = prop->any_freq_max;
+	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->idx, pin,
+		       xa_limit_16b, GFP_KERNEL);
+release:
+	if (ret) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		kfree(pin->prop.description);
+		kfree(pin->rclk_dev_name);
+		kfree(pin);
+		return ERR_PTR(ret);
+	}
+
+	return pin;
+}
+
+/**
+ * dpll_pin_get - find existing or create new dpll pin
+ * @clock_id: clock_id of creator
+ * @dev_driver_id: id 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 device_drv_id, struct module *module,
+	     const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pos, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_pin_xa_lock);
+	xa_for_each(&dpll_pin_xa, index, pos) {
+		if (pos->clock_id == clock_id &&
+		    pos->dev_driver_id == device_drv_id &&
+		    pos->module == module) {
+			ret = pos;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	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;
+	mutex_lock(&dpll_pin_xa_lock);
+	if (refcount_dec_and_test(&pin->refcount)) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		xa_erase(&dpll_pin_xa, pin->idx);
+		kfree(pin->prop.description);
+		kfree(pin->rclk_dev_name);
+		kfree(pin);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_put);
+
+static int
+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		    struct dpll_pin_ops *ops, void *priv,
+		    const char *rclk_device_name)
+{
+	int ret;
+
+	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);
+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,
+		  struct dpll_pin_ops *ops, void *priv,
+		  struct device *rclk_device)
+{
+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
+	int ret;
+
+	if (WARN_ON(!dpll))
+		return -EINVAL;
+	if (WARN_ON(!pin))
+		return -EINVAL;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+static void
+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
+}
+
+/**
+ * dpll_pin_unregister - deregister dpll pin from dpll device
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ *
+ * Note: It does not free the memory
+ */
+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
+		return -ENOENT;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	__dpll_pin_unregister(dpll, pin);
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return 0;
+}
+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,
+			 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(!pin || !parent))
+		return -EINVAL;
+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
+		return -EPERM;
+	mutex_lock(&dpll_pin_xa_lock);
+	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_device_xa_lock);
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
+					  rclk_device ?
+					  dev_name(rclk_device) : NULL);
+		mutex_unlock(&dpll_device_xa_lock);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref) {
+		if (i < stop) {
+			mutex_lock(&dpll_device_xa_lock);
+			__dpll_pin_unregister(ref->dpll, pin);
+			mutex_unlock(&dpll_device_xa_lock);
+		}
+	}
+	refcount_dec(&pin->refcount);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+unlock:
+	mutex_unlock(&dpll_pin_xa_lock);
+	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
+ *
+ * Note: It does not free the memory
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	mutex_lock(&dpll_device_xa_lock);
+	mutex_lock(&dpll_pin_xa_lock);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	refcount_dec(&pin->refcount);
+	xa_for_each(&pin->dpll_refs, i, ref) {
+		__dpll_pin_unregister(ref->dpll, pin);
+		dpll_pin_parent_notify(ref->dpll, pin, parent,
+				       DPLL_A_PIN_IDX);
+	}
+	mutex_unlock(&dpll_pin_xa_lock);
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+/**
+ * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
+ * @dpll: dpll device pointer
+ * @idx: index of pin
+ *
+ * Find a reference to a pin registered with given dpll and return its pointer.
+ *
+ * Return:
+ * * valid pointer if pin was found
+ * * NULL if not found
+ */
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
+{
+	struct dpll_pin_ref *pos;
+	unsigned long i;
+
+	xa_for_each(&dpll->pin_refs, i, pos) {
+		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
+			return pos->pin;
+	}
+
+	return NULL;
+}
+
+/**
+ * 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)
+{
+	return dpll->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_priv);
+
+/**
+ * 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_ref *ref;
+
+	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
+
+	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	ret = class_register(&dpll_class);
+	if (ret)
+		goto unregister_netlink;
+
+	return 0;
+
+unregister_netlink:
+	dpll_netlink_finish();
+error:
+	mutex_destroy(&dpll_device_xa_lock);
+	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,99 @@
+/* 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/refcount.h>
+#include "dpll_netlink.h"
+
+#define DPLL_REGISTERED		XA_MARK_1
+
+/**
+ * struct dpll_device - structure for a DPLL device
+ * @id:			unique id number for each device
+ * @dev_driver_id:	id given by dev driver
+ * @dev:		struct device for this dpll device
+ * @parent:		parent device
+ * @module:		module of creator
+ * @ops:		operations this &dpll_device supports
+ * @lock:		mutex to serialize operations
+ * @type:		type of a dpll
+ * @priv:		pointer to private information of owner
+ * @pins:		list of pointers to pins registered with this dpll
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @mode_supported_mask: mask of supported modes
+ * @refcount:		refcount
+ **/
+struct dpll_device {
+	u32 id;
+	u32 dev_driver_id;
+	struct device dev;
+	struct device *parent;
+	struct module *module;
+	struct dpll_device_ops *ops;
+	enum dpll_type type;
+	void *priv;
+	struct xarray pin_refs;
+	u64 clock_id;
+	unsigned long mode_supported_mask;
+	refcount_t refcount;
+};
+
+/**
+ * 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 idx;
+	u32 dev_driver_id;
+	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_ref - structure for referencing either dpll or pins
+ * @dpll:		pointer to a dpll
+ * @pin:		pointer to a pin
+ * @ops:		ops for a dpll pin
+ * @priv:		pointer to private information of owner
+ **/
+struct dpll_pin_ref {
+	union {
+		struct dpll_device *dpll;
+		struct dpll_pin *pin;
+	};
+	struct dpll_pin_ops *ops;
+	void *priv;
+	refcount_t refcount;
+};
+
+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);
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
+struct dpll_pin_ref *
+dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
+struct dpll_pin_ref *
+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
+extern struct xarray dpll_device_xa;
+extern struct xarray dpll_pin_xa;
+extern struct mutex dpll_device_xa_lock;
+extern struct mutex dpll_pin_xa_lock;
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..46aefeb1ac93
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,1065 @@
+// 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>
+
+static u32 dpll_pin_freq_value[] = {
+	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
+	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
+};
+
+static int
+dpll_msg_add_dev_handle(struct sk_buff *msg, const 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, const struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	enum dpll_mode mode;
+
+	if (WARN_ON(!dpll->ops->mode_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->mode_get(dpll, &mode, extack))
+		return -EFAULT;
+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
+			    struct netlink_ext_ack *extack)
+{
+	u32 source_pin_idx;
+
+	if (WARN_ON(!dpll->ops->source_pin_idx_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
+		return -EFAULT;
+	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	enum dpll_lock_status status;
+
+	if (WARN_ON(!dpll->ops->lock_status_get))
+		return -EOPNOTSUPP;
+	if (dpll->ops->lock_status_get(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)
+{
+	s32 temp;
+
+	if (!dpll->ops->temp_get)
+		return -EOPNOTSUPP;
+	if (dpll->ops->temp_get(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)
+{
+	u32 prio;
+
+	if (!ref->ops->prio_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->prio_get(pin, ref->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)
+{
+	enum dpll_pin_state state;
+
+	if (!ref->ops->state_on_dpll_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_dpll_get(pin, ref->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 netlink_ext_ack *extack)
+{
+	enum dpll_pin_direction direction;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
+		if (ref && ref->ops && ref->dpll)
+			break;
+	}
+	if (!ref || !ref->ops || !ref->dpll)
+		return -ENODEV;
+	if (!ref->ops->direction_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
+{
+	enum dpll_pin_freq_supp fs;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	u32 freq;
+
+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
+		if (ref && ref->ops && ref->dpll)
+			break;
+	}
+	if (!ref || !ref->ops || !ref->dpll)
+		return -ENODEV;
+	if (!ref->ops->frequency_get)
+		return -EOPNOTSUPP;
+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
+		return -EFAULT;
+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
+		return -EMSGSIZE;
+	if (!dump_any_freq)
+		return 0;
+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
+		if (test_bit(fs, &pin->prop.freq_supported)) {
+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
+			    dpll_pin_freq_value[fs]))
+				return -EMSGSIZE;
+		}
+	}
+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
+				pin->prop.any_freq_min))
+			return -EMSGSIZE;
+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
+				pin->prop.any_freq_max))
+			return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref_parent;
+	enum dpll_pin_state state;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref_parent) {
+		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
+			return -EFAULT;
+		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
+							&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,
+				ref_parent->pin->dev_driver_id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref = NULL;
+	enum dpll_pin_state state;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref) {
+		if (WARN_ON(!ref->ops->state_on_pin_get))
+			return -EFAULT;
+		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
+				ref->pin->dev_driver_id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
+		       struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	struct nlattr *attr;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->dpll_refs, index, ref) {
+		attr = nla_nest_start(msg, DPLL_A_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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
+		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, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
+	if (ret)
+		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, bool dump_dpll)
+{
+	int ret;
+
+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
+		return -EMSGSIZE;
+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
+	if (ret)
+		return ret;
+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
+		if (ret)
+			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_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
+		     struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	enum dpll_mode mode;
+	unsigned long i;
+	int ret;
+
+	ret = dpll_msg_add_dev_handle(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_source_pin_idx(msg, dpll, extack);
+	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;
+	xa_for_each(&dpll->pin_refs, i, ref) {
+		struct nlattr *nest = nla_nest_start(msg, DPLL_A_PIN);
+
+		if (!nest) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_cmd_pin_on_dpll_get(msg, ref->pin, dpll, extack);
+		if (ret) {
+			nla_nest_cancel(msg, nest);
+			break;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return ret;
+}
+
+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
+{
+	enum dpll_pin_freq_supp fs;
+
+	if (freq >= pin->prop.any_freq_min && freq <= pin->prop.any_freq_max)
+		return true;
+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++)
+		if (test_bit(fs, &pin->prop.freq_supported))
+			if (freq == dpll_pin_freq_value[fs])
+				return true;
+	return false;
+}
+
+static int
+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
+		  struct netlink_ext_ack *extack)
+{
+	u32 freq = nla_get_u32(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) {
+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
+		if (ret)
+			return -EFAULT;
+		dpll_pin_notify(ref->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)
+{
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *parent;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	parent = dpll_pin_get_by_idx(dpll, parent_idx);
+	if (!parent)
+		return -EINVAL;
+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
+	if (!ref)
+		return -EINVAL;
+	if (!ref->ops || !ref->ops->state_on_pin_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
+		return -EFAULT;
+	dpll_pin_parent_notify(dpll, pin, parent, 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)
+{
+	struct dpll_pin_ref *ref;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
+		return -EOPNOTSUPP;
+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	if (!ref->ops || !ref->ops->state_on_dpll_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
+		return -EINVAL;
+	dpll_pin_notify(ref->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)
+{
+	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 = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+	if (!ref)
+		return -EFAULT;
+	if (!ref->ops || !ref->ops->prio_set)
+		return -EOPNOTSUPP;
+	if (ref->ops->prio_set(pin, 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) {
+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
+			return -EFAULT;
+		dpll_pin_notify(ref->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;
+	u32 parent_idx = PIN_IDX_INVALID;
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLL_A_PIN_FREQUENCY:
+			ret = dpll_pin_freq_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_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_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_idx == PIN_IDX_INVALID) {
+			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 nlattr *hdr, *nest;
+	struct sk_buff *msg;
+	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;
+	nest = nla_nest_start(msg, DPLL_A_PIN);
+	if (!nest)
+		return -EMSGSIZE;
+	ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack, true);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	nla_nest_end(msg, nest);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct nlattr *hdr, *nest;
+	struct dpll_pin *pin;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_nl_family, 0, DPLL_CMD_PIN_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	xa_for_each(&dpll_pin_xa, i, pin) {
+		if (xa_empty(&pin->dpll_refs))
+			continue;
+		nest = nla_nest_start(skb, DPLL_A_PIN);
+		if (!nest) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack, true);
+		if (ret) {
+			nla_nest_cancel(skb, nest);
+			break;
+		}
+		nla_nest_end(skb, nest);
+	}
+
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	return ret;
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	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);
+
+			if (!dpll->ops || !dpll->ops->mode_set)
+				return -EOPNOTSUPP;
+			ret = dpll->ops->mode_set(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 nlattr *hdr, *nest;
+	struct sk_buff *msg;
+	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;
+
+	nest = nla_nest_start(msg, DPLL_A_DEVICE);
+	ret = dpll_device_get_one(dpll, msg, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	nla_nest_end(msg, nest);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct nlattr *hdr, *nest;
+	struct dpll_device *dpll;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
+		ret = dpll_msg_add_dev_handle(skb, dpll);
+		if (ret) {
+			nla_nest_cancel(skb, nest);
+			break;
+		}
+		nla_nest_end(skb, nest);
+	}
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	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, *dpll_name = NULL;
+	int ret = -ENODEV;
+
+	if (!info->attrs[DPLL_A_ID] &&
+	    !(info->attrs[DPLL_A_BUS_NAME] && info->attrs[DPLL_A_DEV_NAME]))
+		return -EINVAL;
+
+	mutex_lock(&dpll_device_xa_lock);
+	if (info->attrs[DPLL_A_ID]) {
+		u32 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;
+	}
+	if (info->attrs[DPLL_A_BUS_NAME] &&
+	    info->attrs[DPLL_A_DEV_NAME]) {
+		const char *bus_name = nla_data(info->attrs[DPLL_A_BUS_NAME]);
+		const char *dev_name = nla_data(info->attrs[DPLL_A_DEV_NAME]);
+
+		dpll_name = dpll_device_get_by_name(bus_name, dev_name);
+		if (!dpll_name) {
+			ret = -ENODEV;
+			goto unlock;
+		}
+
+		if (dpll_id && dpll_name != dpll_id)
+			goto unlock;
+		info->user_ptr[0] = dpll_name;
+	}
+
+	return 0;
+unlock:
+	mutex_unlock(&dpll_device_xa_lock);
+	return ret;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_device_xa_lock);
+}
+
+int dpll_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_device_xa_lock);
+
+	return 0;
+}
+
+int dpll_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_device_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_device *dpll;
+	struct dpll_pin *pin;
+
+	if (ret)
+		return ret;
+	dpll = info->user_ptr[0];
+	if (!info->attrs[DPLL_A_PIN_IDX]) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	mutex_lock(&dpll_pin_xa_lock);
+	pin = dpll_pin_get_by_idx(dpll,
+				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
+	if (!pin) {
+		ret = -ENODEV;
+		goto unlock_pin;
+	}
+	info->user_ptr[1] = pin;
+
+	return 0;
+
+unlock_pin:
+	mutex_unlock(&dpll_pin_xa_lock);
+unlock_dev:
+	mutex_unlock(&dpll_device_xa_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	mutex_unlock(&dpll_pin_xa_lock);
+	dpll_post_doit(ops, skb, info);
+}
+
+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_pin_xa_lock);
+
+	return dpll_pre_dumpit(cb);
+}
+
+int dpll_pin_post_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_pin_xa_lock);
+
+	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->dev_driver_id))
+		return -EMSGSIZE;
+
+	switch (attr) {
+	case DPLL_A_MODE:
+		ret = dpll_msg_add_mode(msg, dpll, NULL);
+		break;
+	case DPLL_A_SOURCE_PIN_IDX:
+		ret = dpll_msg_add_source_pin_idx(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:
+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
+		break;
+	case DPLL_A_PIN_PRIO:
+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+		if (!ref)
+			return -EFAULT;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
+		break;
+	case DPLL_A_PIN_STATE:
+		if (parent) {
+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
+			if (!ref)
+				return -EFAULT;
+			if (!ref->ops || !ref->ops->state_on_pin_get)
+				return -EOPNOTSUPP;
+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
+							 NULL);
+			if (ret)
+				return ret;
+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
+					parent->dev_driver_id))
+				return -EMSGSIZE;
+		} else {
+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+			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);
+}
+
+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..072efa10f0e6
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,30 @@
+/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dplla attr);
+
+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..db98b6d4bb73
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. 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;
+
+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
+			struct netlink_ext_ack *extack);
+	int (*mode_set)(const struct dpll_device *dpll,
+			const enum dpll_mode mode,
+			struct netlink_ext_ack *extack);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+			       const enum dpll_mode mode,
+			       struct netlink_ext_ack *extack);
+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
+				  u32 *pin_idx,
+				  struct netlink_ext_ack *extack);
+	int (*lock_status_get)(const struct dpll_device *dpll,
+			       enum dpll_lock_status *status,
+			       struct netlink_ext_ack *extack);
+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
+			struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_ops {
+	int (*frequency_set)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     const u32 frequency,
+			     struct netlink_ext_ack *extack);
+	int (*frequency_get)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     u32 *frequency, struct netlink_ext_ack *extack);
+	int (*direction_set)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     const enum dpll_pin_direction direction,
+			     struct netlink_ext_ack *extack);
+	int (*direction_get)(const struct dpll_pin *pin,
+			     const struct dpll_device *dpll,
+			     enum dpll_pin_direction *direction,
+			     struct netlink_ext_ack *extack);
+	int (*state_on_pin_get)(const struct dpll_pin *pin,
+				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,
+				 const struct dpll_device *dpll,
+				 enum dpll_pin_state *state,
+				 struct netlink_ext_ack *extack);
+	int (*state_on_pin_set)(const struct dpll_pin *pin,
+				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,
+				 const struct dpll_device *dpll,
+				 const enum dpll_pin_state state,
+				 struct netlink_ext_ack *extack);
+	int (*prio_get)(const struct dpll_pin *pin,
+			const struct dpll_device *dpll,
+			u32 *prio, struct netlink_ext_ack *extack);
+	int (*prio_set)(const struct dpll_pin *pin,
+			const struct dpll_device *dpll,
+			const u32 prio, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_properties {
+	const char *description;
+	enum dpll_pin_type type;
+	unsigned long freq_supported;
+	u32 any_freq_min;
+	u32 any_freq_max;
+	unsigned long capabilities;
+};
+
+enum dpll_pin_freq_supp {
+	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
+	DPLL_PIN_FREQ_SUPP_1_HZ,
+	DPLL_PIN_FREQ_SUPP_10_MHZ,
+
+	__DPLL_PIN_FREQ_SUPP_MAX,
+	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
+};
+
+/**
+ * dpll_device_get - find or create dpll_device object
+ * @clock_id: a system unique number for a device
+ * @dev_driver_idx: 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,
+			 struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
+
+/**
+ * dpll_device_unregister - deregister registered dpll
+ * @dpll: pointer to dpll
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll);
+
+/**
+ * dpll_priv - get dpll private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private data pointer passed to dpll subsystem when allocating
+ * device with ``dpll_device_alloc(..)``
+ */
+void *dpll_priv(const struct dpll_device *dpll);
+
+/**
+ * dpll_pin_on_pin_priv - get pin on pin pair private data
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a dpll_pin
+ *
+ * Obtain private pin data pointer passed to dpll subsystem when pin
+ * was registered with parent pin.
+ */
+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_on_dpll_priv - get pin on dpll pair private data
+ * @dpll: pointer to dpll
+ * @pin: pointer to a dpll_pin
+ *
+ * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
+ * was registered with a dpll.
+ */
+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_get - get reference or create new pin object
+ * @clock_id: a system unique number of a device
+ * @dev_driver_idx: 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_idx 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 *pin_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,
+		      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
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ * Return:
+ * * 0 - pin was successfully deregistered from this dpll device,
+ * * -ENXIO - given pin was not registered with this dpll device,
+ * * -EINVAL - pin pointer is not valid.
+ */
+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * 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,
+			     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);
+
+/**
+ * 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);
+
+
+#endif
-- 
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] 202+ messages in thread

* [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-12  2:28   ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 348 insertions(+)
 create mode 100644 Documentation/networking/dpll.rst

diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
new file mode 100644
index 000000000000..25cd81edc73c
--- /dev/null
+++ b/Documentation/networking/dpll.rst
@@ -0,0 +1,347 @@
+.. 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.
+
+
+Dpll object
+===========
+Single dpll device object means single Digital PLL circuit and bunch of
+pins connected with it.
+It provides its capablities 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 input 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 properities and capabilities are 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 either
+with dpll or different pin 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.
+
+
+Shared pins
+===========
+Pin can be shared by multiple dpll devices. Where configuration on one
+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).
+
+
+MUX-type pins
+=============
+A pin can be MUX-type, which aggregates child pins and serves as pin
+multiplexer. One or more pins are attached to MUX-type instead of being
+directly connected to a dpll device.
+Pins registered with a MUX-type provides user with additional nested
+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered
+with.
+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.
+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 are not capable of automatic source pin selection,
+in order to configure a source of a MUX-type pin the user still needs
+to request desired pin state.
+
+
+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 command 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
+    ``SOURCE_PIN_IDX``          attr index of currently selected source
+    ``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_PRIO``                attr pin priority 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. Possible response message attributes for netlink dump
+requests:
+
+  ==============================  =======================================
+  ``PIN_GET``                     command to dump pins
+    ``PIN``                       attr nested type contains single pin
+      ``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_IDX``                 attr index of dumped pin (on dplls)
+      ``PIN_DESCRIPTION``         description of a pin provided by driver
+      ``PIN_TYPE``                attr value of pin type
+      ``PIN_FREQUENCY``           attr current frequency of a pin
+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
+                                  recovers clock signal from
+      ``PIN_DIRECTION``           attr direction of a pin
+      ``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
+
+  ``DEVICE_GET``                  command to dump dplls
+    ``DEVICE``                    attr nested type contatin a single
+                                  dpll device
+      ``ID``                      attr internal dpll device ID
+      ``DEV_NAME``                attr dpll device name
+      ``BUS_NAME``                attr dpll device bus name
+
+
+Dpll 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 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 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 used to provide pulse-per-second output
+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
+
+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 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 id) use ``dpll_device_get``
+to obtain proper dpll device pointer.
+
+The name od DPLL device is generated based on registerer provided device
+struct pointer and dev_driver_id value.
+Name is in format: ``%s_%u`` witch arguments:
+``dev_name(struct device *)`` - syscall on parent device struct
+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
+driver with ``dpll_device_notify`` corresponding attribute as a reason.
+
+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 4ddcae33c336..6eb83a47cc2d 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] 202+ messages in thread

* [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
@ 2023-03-12  2:28   ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 348 insertions(+)
 create mode 100644 Documentation/networking/dpll.rst

diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
new file mode 100644
index 000000000000..25cd81edc73c
--- /dev/null
+++ b/Documentation/networking/dpll.rst
@@ -0,0 +1,347 @@
+.. 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.
+
+
+Dpll object
+===========
+Single dpll device object means single Digital PLL circuit and bunch of
+pins connected with it.
+It provides its capablities 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 input 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 properities and capabilities are 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 either
+with dpll or different pin 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.
+
+
+Shared pins
+===========
+Pin can be shared by multiple dpll devices. Where configuration on one
+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).
+
+
+MUX-type pins
+=============
+A pin can be MUX-type, which aggregates child pins and serves as pin
+multiplexer. One or more pins are attached to MUX-type instead of being
+directly connected to a dpll device.
+Pins registered with a MUX-type provides user with additional nested
+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered
+with.
+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.
+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 are not capable of automatic source pin selection,
+in order to configure a source of a MUX-type pin the user still needs
+to request desired pin state.
+
+
+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 command 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
+    ``SOURCE_PIN_IDX``          attr index of currently selected source
+    ``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_PRIO``                attr pin priority 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. Possible response message attributes for netlink dump
+requests:
+
+  ==============================  =======================================
+  ``PIN_GET``                     command to dump pins
+    ``PIN``                       attr nested type contains single pin
+      ``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_IDX``                 attr index of dumped pin (on dplls)
+      ``PIN_DESCRIPTION``         description of a pin provided by driver
+      ``PIN_TYPE``                attr value of pin type
+      ``PIN_FREQUENCY``           attr current frequency of a pin
+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
+                                  recovers clock signal from
+      ``PIN_DIRECTION``           attr direction of a pin
+      ``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
+
+  ``DEVICE_GET``                  command to dump dplls
+    ``DEVICE``                    attr nested type contatin a single
+                                  dpll device
+      ``ID``                      attr internal dpll device ID
+      ``DEV_NAME``                attr dpll device name
+      ``BUS_NAME``                attr dpll device bus name
+
+
+Dpll 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 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 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 used to provide pulse-per-second output
+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
+
+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 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 id) use ``dpll_device_get``
+to obtain proper dpll device pointer.
+
+The name od DPLL device is generated based on registerer provided device
+struct pointer and dev_driver_id value.
+Name is in format: ``%s_%u`` witch arguments:
+``dev_name(struct device *)`` - syscall on parent device struct
+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
+driver with ``dpll_device_notify`` corresponding attribute as a reason.
+
+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 4ddcae33c336..6eb83a47cc2d 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] 202+ messages in thread

* [PATCH RFC v6 4/6] ice: add admin commands to access cgu configuration
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-12  2:28   ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel, 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 b0e29e342401..116eb64db969 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -203,6 +203,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 c2fda4fa4188..9c2ccb2072fd 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
@@ -4926,6 +5003,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 781475480ff2..8738eb627fd1 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4322,13 +4322,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 e3f622cad425..c49f573d724f 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -962,6 +962,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] 202+ messages in thread

* [PATCH RFC v6 4/6] ice: add admin commands to access cgu configuration
@ 2023-03-12  2:28   ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel, 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 b0e29e342401..116eb64db969 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -203,6 +203,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 c2fda4fa4188..9c2ccb2072fd 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
@@ -4926,6 +5003,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 781475480ff2..8738eb627fd1 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4322,13 +4322,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 e3f622cad425..c49f573d724f 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -962,6 +962,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] 202+ messages in thread

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

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 | 1845 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |   96 ++
 drivers/net/ethernet/intel/ice/ice_main.c |    7 +
 6 files changed, 1955 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index c18c3b373846..d6ea4edd552a 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -301,6 +301,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 116eb64db969..5cc2a99f00b7 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -75,6 +75,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
@@ -202,6 +203,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_PLUG_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..a97ccf5840d5
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,1845 @@
+// 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 CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_LOCK_TRIES		1000
+
+/**
+ * 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
+ * * 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 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 PIN_IDX_INVALID;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return i;
+
+	return 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: Board private 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
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ * @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,
+		       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 = dpll_pin_on_dpll_priv(dpll, pin);
+	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
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_frequency_set - output pin callback for set frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ * @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,
+		       const struct dpll_device *dpll,
+		       u32 *frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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 = 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
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_frequency_get - output pin callback for get frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, 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 mode, frequency and signal type. Then update struct
+ * holding the pin info.
+ *
+ * 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]))
+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
+		else
+			pin->state[0] = 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,
+				    enum dpll_lock_status *status,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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_source_idx_get - get dpll's source index
+ * @dpll: registered dpll pointer
+ * @pin_idx: on success holds currently selected source pin index
+ *
+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
+ * with.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx,
+				   struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		ice_dpll_cb_unlock(pf);
+		return -EFAULT;
+	}
+	if (d->dpll_state == ICE_CGU_STATE_INVALID ||
+	    d->dpll_state == ICE_CGU_STATE_FREERUN)
+		*pin_idx = PIN_IDX_INVALID;
+	else
+		*pin_idx = (u32)d->source_idx;
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
+		__func__, dpll, pf, d, *pin_idx);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @mode: on success holds current working mode of dpll
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll,
+			     enum dpll_mode *mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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
+ * @mode: mode to be checked for support
+ *
+ * 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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of modified pin
+ * @mode: requested mode
+ *
+ * Determine requested pin mode set it 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,
+		       const enum dpll_pin_state state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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 (state == DPLL_PIN_STATE_CONNECTED)
+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+	else
+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
+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
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @state: state to be set
+ *
+ * Dpll subsystem callback. Enables given mode 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,
+				     const struct dpll_device *dpll,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_set - enable/disable source pin on dpll levice
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @state: state to be set
+ *
+ * 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,
+				     const struct dpll_device *dpll,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, 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
+ * @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,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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;
+	}
+	if ((pin_type == ICE_DPLL_PIN_TYPE_SOURCE &&
+	     !!(p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)) ||
+	    (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT &&
+	     !!(p->flags[0] & ICE_AQC_SET_CGU_OUT_CFG_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: 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
+ * @dpll: registered dpll pointer
+ * @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,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_state *state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_get - get source pin state on dpll device
+ * @pin: pointer to a pin
+ * @dpll: registered dpll pointer
+ * @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_source_state_get(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_state *state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: on success - returns source priority on dpll
+ *
+ * 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,
+				    const struct dpll_device *dpll, u32 *prio,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: source priority to be set on dpll
+ *
+ * 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,
+				    const struct dpll_device *dpll,
+				    const u32 prio,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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;
+}
+
+static int ice_dpll_source_direction(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction *direction,
+				     struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_SOURCE;
+
+	return 0;
+}
+
+static int ice_dpll_output_direction(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     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
+ *
+ * dpll subsystem callback, set a state of a rclk pin
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin,
+					  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;
+	struct ice_pf *pf = dpll_pin_on_pin_priv(parent_pin, pin);
+	u32 parent_idx, hw_idx = PIN_IDX_INVALID, i;
+	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 == 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 == 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
+ * @parent_pin: pointer to a parent pin
+ * @state: on success holds valid pin state
+ * @extack: used for 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,
+					  const struct dpll_pin *parent_pin,
+					  enum dpll_pin_state *state,
+					  struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_pin_priv(parent_pin, pin);
+	u32 parent_idx, hw_idx = 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 == 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 == 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,
+	.source_pin_idx_get = ice_dpll_source_idx_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);
+	}
+	dpll_pin_put(rclk->pin);
+	rclk->pin = NULL;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Deregister and free pins of a given array of pins from dpll devices
+ * registered in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * positive - number of errors encounterd on pin's deregistration.
+ */
+static int
+ice_dpll_release_pins(struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count, bool cgu)
+{
+	int i, ret, err = 0;
+
+	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) {
+				ret = dpll_pin_unregister(dpll_eec, p->pin);
+				if (ret)
+					err++;
+			}
+			if (cgu && dpll_pps) {
+				ret = dpll_pin_unregister(dpll_pps, p->pin);
+				if (ret)
+					err++;
+			}
+			dpll_pin_put(p->pin);
+			p->pin = NULL;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * 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
+ * @hw: board 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_hw *hw, struct ice_dpll *d)
+{
+	int ret;
+
+	ret = ice_get_cgu_state(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((struct ice_pf *)(hw->back)),
+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
+		d->dpll_idx, d->source_idx,
+		d->dpll_state, d->prev_dpll_state);
+
+	if (ret)
+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(hw->adminq.sq_last_status));
+
+	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_idx != d->source_idx) {
+		d->prev_source_idx = d->source_idx;
+		dpll_device_notify(d->dpll, DPLL_A_SOURCE_PIN_IDX);
+	}
+}
+
+/**
+ * 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;
+	if (ice_dpll_cb_lock(pf))
+		return;
+	ret = ice_dpll_update_state(&pf->hw, de);
+	if (!ret)
+		ret = ice_dpll_update_state(&pf->hw, dp);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    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);
+	/* 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->hw, &d->eec);
+	ice_dpll_update_state(&pf->hw, &d->pps);
+	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;
+	int ret;
+
+	mutex_lock(&pf->dplls.lock);
+	ice_dpll_release_rclk_pin(pf);
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+				    d->num_inputs, cgu);
+	mutex_unlock(&pf->dplls.lock);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "source pins release dplls err=%d\n", ret);
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+					    d->num_outputs, cgu);
+		mutex_unlock(&pf->dplls.lock);
+		if (ret)
+			dev_warn(ice_pf_to_dev(pf),
+				 "output pins release on dplls err=%d\n", ret);
+	}
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(dp->dpll);
+		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);
+		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
+ *
+ * This function handles the cleanup work required from the initialization by
+ * 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;
+	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.description = 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.any_freq_min = 0;
+		pins[i].prop.any_freq_max = 0;
+		pins[i].prop.freq_supported = ice_cgu_get_pin_freq_mask(hw, i,
+									input);
+	}
+
+	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.description = 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..defe54262ab9
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,96 @@
+/* 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
+ */
+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;
+};
+
+/** 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;
+	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 567694bf098b..1af7b2ba4665 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4810,6 +4810,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");
@@ -4836,6 +4840,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)
-- 
2.34.1


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

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

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 | 1845 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |   96 ++
 drivers/net/ethernet/intel/ice/ice_main.c |    7 +
 6 files changed, 1955 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index c18c3b373846..d6ea4edd552a 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -301,6 +301,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 116eb64db969..5cc2a99f00b7 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -75,6 +75,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
@@ -202,6 +203,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_PLUG_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..a97ccf5840d5
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,1845 @@
+// 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 CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_LOCK_TRIES		1000
+
+/**
+ * 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
+ * * 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 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 PIN_IDX_INVALID;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return i;
+
+	return 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: Board private 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
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ * @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,
+		       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 = dpll_pin_on_dpll_priv(dpll, pin);
+	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
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_frequency_set - output pin callback for set frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: frequency to be set
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ * @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,
+		       const struct dpll_device *dpll,
+		       u32 *frequency,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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 = 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
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, dpll, frequency, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_frequency_get - output pin callback for get frequency
+ * @pin: pointer to a pin
+ * @dpll: pointer to dpll
+ * @frequency: on success holds pin's frequency
+ * @extack: netlink extack
+ *
+ * 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,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, 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 mode, frequency and signal type. Then update struct
+ * holding the pin info.
+ *
+ * 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]))
+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
+		else
+			pin->state[0] = 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,
+				    enum dpll_lock_status *status,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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_source_idx_get - get dpll's source index
+ * @dpll: registered dpll pointer
+ * @pin_idx: on success holds currently selected source pin index
+ *
+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
+ * with.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx,
+				   struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+
+	if (!pf)
+		return -EINVAL;
+
+	if (ice_dpll_cb_lock(pf))
+		return -EBUSY;
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		ice_dpll_cb_unlock(pf);
+		return -EFAULT;
+	}
+	if (d->dpll_state == ICE_CGU_STATE_INVALID ||
+	    d->dpll_state == ICE_CGU_STATE_FREERUN)
+		*pin_idx = PIN_IDX_INVALID;
+	else
+		*pin_idx = (u32)d->source_idx;
+	ice_dpll_cb_unlock(pf);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
+		__func__, dpll, pf, d, *pin_idx);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @mode: on success holds current working mode of dpll
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll,
+			     enum dpll_mode *mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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
+ * @mode: mode to be checked for support
+ *
+ * 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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	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
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of modified pin
+ * @mode: requested mode
+ *
+ * Determine requested pin mode set it 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,
+		       const enum dpll_pin_state state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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 (state == DPLL_PIN_STATE_CONNECTED)
+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+	else
+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type);
+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
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @state: state to be set
+ *
+ * Dpll subsystem callback. Enables given mode 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,
+				     const struct dpll_device *dpll,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_set - enable/disable source pin on dpll levice
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @state: state to be set
+ *
+ * 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,
+				     const struct dpll_device *dpll,
+				     const enum dpll_pin_state state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_set(dpll, pin, 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
+ * @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,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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;
+	}
+	if ((pin_type == ICE_DPLL_PIN_TYPE_SOURCE &&
+	     !!(p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)) ||
+	    (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT &&
+	     !!(p->flags[0] & ICE_AQC_SET_CGU_OUT_CFG_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: 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
+ * @dpll: registered dpll pointer
+ * @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,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_state *state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_state_get - get source pin state on dpll device
+ * @pin: pointer to a pin
+ * @dpll: registered dpll pointer
+ * @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_source_state_get(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_state *state,
+				     struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(dpll, pin, state, extack,
+				      ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: on success - returns source priority on dpll
+ *
+ * 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,
+				    const struct dpll_device *dpll, u32 *prio,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: source priority to be set on dpll
+ *
+ * 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,
+				    const struct dpll_device *dpll,
+				    const u32 prio,
+				    struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_dpll_priv(dpll, pin);
+	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;
+}
+
+static int ice_dpll_source_direction(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction *direction,
+				     struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_SOURCE;
+
+	return 0;
+}
+
+static int ice_dpll_output_direction(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     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
+ *
+ * dpll subsystem callback, set a state of a rclk pin
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin,
+					  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;
+	struct ice_pf *pf = dpll_pin_on_pin_priv(parent_pin, pin);
+	u32 parent_idx, hw_idx = PIN_IDX_INVALID, i;
+	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 == 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 == 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
+ * @parent_pin: pointer to a parent pin
+ * @state: on success holds valid pin state
+ * @extack: used for 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,
+					  const struct dpll_pin *parent_pin,
+					  enum dpll_pin_state *state,
+					  struct netlink_ext_ack *extack)
+{
+	struct ice_pf *pf = dpll_pin_on_pin_priv(parent_pin, pin);
+	u32 parent_idx, hw_idx = 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 == 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 == 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,
+	.source_pin_idx_get = ice_dpll_source_idx_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);
+	}
+	dpll_pin_put(rclk->pin);
+	rclk->pin = NULL;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Deregister and free pins of a given array of pins from dpll devices
+ * registered in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * positive - number of errors encounterd on pin's deregistration.
+ */
+static int
+ice_dpll_release_pins(struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count, bool cgu)
+{
+	int i, ret, err = 0;
+
+	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) {
+				ret = dpll_pin_unregister(dpll_eec, p->pin);
+				if (ret)
+					err++;
+			}
+			if (cgu && dpll_pps) {
+				ret = dpll_pin_unregister(dpll_pps, p->pin);
+				if (ret)
+					err++;
+			}
+			dpll_pin_put(p->pin);
+			p->pin = NULL;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * 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
+ * @hw: board 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_hw *hw, struct ice_dpll *d)
+{
+	int ret;
+
+	ret = ice_get_cgu_state(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((struct ice_pf *)(hw->back)),
+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
+		d->dpll_idx, d->source_idx,
+		d->dpll_state, d->prev_dpll_state);
+
+	if (ret)
+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(hw->adminq.sq_last_status));
+
+	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_idx != d->source_idx) {
+		d->prev_source_idx = d->source_idx;
+		dpll_device_notify(d->dpll, DPLL_A_SOURCE_PIN_IDX);
+	}
+}
+
+/**
+ * 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;
+	if (ice_dpll_cb_lock(pf))
+		return;
+	ret = ice_dpll_update_state(&pf->hw, de);
+	if (!ret)
+		ret = ice_dpll_update_state(&pf->hw, dp);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    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);
+	/* 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->hw, &d->eec);
+	ice_dpll_update_state(&pf->hw, &d->pps);
+	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;
+	int ret;
+
+	mutex_lock(&pf->dplls.lock);
+	ice_dpll_release_rclk_pin(pf);
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+				    d->num_inputs, cgu);
+	mutex_unlock(&pf->dplls.lock);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "source pins release dplls err=%d\n", ret);
+	if (cgu) {
+		mutex_lock(&pf->dplls.lock);
+		ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+					    d->num_outputs, cgu);
+		mutex_unlock(&pf->dplls.lock);
+		if (ret)
+			dev_warn(ice_pf_to_dev(pf),
+				 "output pins release on dplls err=%d\n", ret);
+	}
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		mutex_lock(&pf->dplls.lock);
+		if (cgu)
+			dpll_device_unregister(dp->dpll);
+		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);
+		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
+ *
+ * This function handles the cleanup work required from the initialization by
+ * 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;
+	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.description = 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.any_freq_min = 0;
+		pins[i].prop.any_freq_max = 0;
+		pins[i].prop.freq_supported = ice_cgu_get_pin_freq_mask(hw, i,
+									input);
+	}
+
+	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.description = 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..defe54262ab9
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,96 @@
+/* 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
+ */
+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;
+};
+
+/** 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;
+	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 567694bf098b..1af7b2ba4665 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4810,6 +4810,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");
@@ -4836,6 +4840,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)
-- 
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] 202+ messages in thread

* [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-12  2:28   ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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 | 209 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 200 insertions(+), 10 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
+#include <uapi/linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
 	bool	fixed_dir;
 	bool	disabled;
 	u8	default_fcn;
+	struct dpll_pin *dpll_pin;
 };
 
 struct ocp_attr_group {
@@ -353,6 +356,7 @@ struct ptp_ocp {
 	struct ptp_ocp_signal	signal[4];
 	struct ptp_ocp_sma_connector sma[4];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 
 static int
-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
 {
 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
-	enum ptp_ocp_sma_mode mode;
-	int val;
-
-	mode = sma->mode;
-	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
-	if (val < 0)
-		return val;
 
 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
 		return -EOPNOTSUPP;
@@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
 	return val;
 }
 
+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+	enum ptp_ocp_sma_mode mode;
+	int val;
+
+	mode = sma->mode;
+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+	if (val < 0)
+		return val;
+
+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
 static ssize_t
 sma1_store(struct device *dev, struct device_attribute *attr,
 	   const char *buf, size_t count)
@@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
+{
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (bp->sma[i].dpll_pin == pin)
+			return i;
+	}
+	return -1;
+}
+
+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+				    enum dpll_lock_status *status,
+				    struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	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,
+				    u32 *idx, struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+
+	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,
+				    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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	if (mode == DPLL_MODE_AUTOMATIC)
+		return true;
+
+	return false;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction *direction,
+				     struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	*direction = bp->sma[sma_nr].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,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction direction,
+				     struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	enum ptp_ocp_sma_mode mode;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	int val = frequency == 10000000 ? 0 : 1;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+
+	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	u32 val;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	val = bp->sma_op->get(bp, sma_nr);
+	if (!val)
+		*frequency = 1000000;
+	else
+		*frequency = 0;
+	return 0;
+}
+
+static 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 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 dpll_pin_properties prop;
 	struct devlink *devlink;
+	char sma[4] = "SMA0";
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
+	u64 clkid;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4226,8 +4377,44 @@ 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 (!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;
+
+	prop.description = &sma[0];
+	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
+	prop.type = DPLL_PIN_TYPE_EXT;
+	prop.any_freq_max = 10000000;
+	prop.any_freq_min = 0;
+	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
+
+	for (i = 0; i < 4; i++) {
+		sma[3] = 0x31 + i;
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
+		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
+			bp->sma[i].dpll_pin = NULL;
+			goto out_dpll;
+		}
+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
+		if (err)
+			goto out_dpll;
+	}
+
+	return 0;
+out_dpll:
+	for (i = 0; i < 4; i++) {
+		if (bp->sma[i].dpll_pin)
+			dpll_pin_put(bp->sma[i].dpll_pin);
+	}
+	dpll_device_put(bp->dpll);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
 
+	dpll_device_unregister(bp->dpll);
+	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] 202+ messages in thread

* [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-12  2:28   ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-12  2:28 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

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 | 209 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 200 insertions(+), 10 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
+#include <uapi/linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
 	bool	fixed_dir;
 	bool	disabled;
 	u8	default_fcn;
+	struct dpll_pin *dpll_pin;
 };
 
 struct ocp_attr_group {
@@ -353,6 +356,7 @@ struct ptp_ocp {
 	struct ptp_ocp_signal	signal[4];
 	struct ptp_ocp_sma_connector sma[4];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 
 static int
-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
 {
 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
-	enum ptp_ocp_sma_mode mode;
-	int val;
-
-	mode = sma->mode;
-	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
-	if (val < 0)
-		return val;
 
 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
 		return -EOPNOTSUPP;
@@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
 	return val;
 }
 
+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+	enum ptp_ocp_sma_mode mode;
+	int val;
+
+	mode = sma->mode;
+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+	if (val < 0)
+		return val;
+
+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
 static ssize_t
 sma1_store(struct device *dev, struct device_attribute *attr,
 	   const char *buf, size_t count)
@@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
+{
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (bp->sma[i].dpll_pin == pin)
+			return i;
+	}
+	return -1;
+}
+
+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+				    enum dpll_lock_status *status,
+				    struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	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,
+				    u32 *idx, struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+
+	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,
+				    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,
+				    const enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	if (mode == DPLL_MODE_AUTOMATIC)
+		return true;
+
+	return false;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction *direction,
+				     struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	*direction = bp->sma[sma_nr].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,
+				     const struct dpll_device *dpll,
+				     enum dpll_pin_direction direction,
+				     struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	enum ptp_ocp_sma_mode mode;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	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,
+			      const struct dpll_device *dpll,
+			      const u32 frequency,
+			      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	int val = frequency == 10000000 ? 0 : 1;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+
+	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+			      const struct dpll_device *dpll,
+			      u32 *frequency,
+			      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
+	u32 val;
+
+	if (sma_nr < 0)
+		return -EINVAL;
+
+	val = bp->sma_op->get(bp, sma_nr);
+	if (!val)
+		*frequency = 1000000;
+	else
+		*frequency = 0;
+	return 0;
+}
+
+static 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 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 dpll_pin_properties prop;
 	struct devlink *devlink;
+	char sma[4] = "SMA0";
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
+	u64 clkid;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4226,8 +4377,44 @@ 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 (!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;
+
+	prop.description = &sma[0];
+	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
+	prop.type = DPLL_PIN_TYPE_EXT;
+	prop.any_freq_max = 10000000;
+	prop.any_freq_min = 0;
+	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
+
+	for (i = 0; i < 4; i++) {
+		sma[3] = 0x31 + i;
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
+		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
+			bp->sma[i].dpll_pin = NULL;
+			goto out_dpll;
+		}
+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
+		if (err)
+			goto out_dpll;
+	}
+
+	return 0;
+out_dpll:
+	for (i = 0; i < 4; i++) {
+		if (bp->sma[i].dpll_pin)
+			dpll_pin_put(bp->sma[i].dpll_pin);
+	}
+	dpll_device_put(bp->dpll);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
 
+	dpll_device_unregister(bp->dpll);
+	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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-12  2:28   ` Vadim Fedorenko
  (?)
@ 2023-03-12  5:12   ` kernel test robot
  -1 siblings, 0 replies; 202+ messages in thread
From: kernel test robot @ 2023-03-12  5:12 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: llvm, oe-kbuild-all

Hi Vadim,

[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on linus/master]
[also build test WARNING on v6.3-rc1 next-20230310]
[cannot apply to tnguy-next-queue/dev-queue]
[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/20230312-103056
patch link:    https://lore.kernel.org/r/20230312022807.278528-7-vadfed%40meta.com
patch subject: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
config: x86_64-randconfig-a005 (https://download.01.org/0day-ci/archive/20230312/202303121343.kC8XYcdk-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/fe16623a48f3c6592be80872758c19822b70ae62
        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/20230312-103056
        git checkout fe16623a48f3c6592be80872758c19822b70ae62
        # 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/

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/202303121343.kC8XYcdk-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/dpll/dpll_core.c:285:1: warning: no previous prototype for function 'dpll_device_alloc' [-Wmissing-prototypes]
   dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)
   ^
   drivers/dpll/dpll_core.c:284:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   struct dpll_device *
   ^
   static 
>> drivers/dpll/dpll_core.c:439:1: warning: no previous prototype for function 'dpll_pin_alloc' [-Wmissing-prototypes]
   dpll_pin_alloc(u64 clock_id, u8 device_drv_id,  struct module *module,
   ^
   drivers/dpll/dpll_core.c:438:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   struct dpll_pin *
   ^
   static 
   2 warnings generated.
--
>> drivers/dpll/dpll_netlink.c:1062:13: warning: no previous prototype for function 'dpll_netlink_fini' [-Wmissing-prototypes]
   void __exit dpll_netlink_fini(void)
               ^
   drivers/dpll/dpll_netlink.c:1062: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.


vim +/dpll_device_alloc +285 drivers/dpll/dpll_core.c

55b810a5137d33 Vadim Fedorenko 2023-03-11  269  
55b810a5137d33 Vadim Fedorenko 2023-03-11  270  
55b810a5137d33 Vadim Fedorenko 2023-03-11  271  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  272   * dpll_device_alloc - allocate the memory for dpll device
55b810a5137d33 Vadim Fedorenko 2023-03-11  273   * @clock_id: clock_id of creator
55b810a5137d33 Vadim Fedorenko 2023-03-11  274   * @dev_driver_id: id given by dev driver
55b810a5137d33 Vadim Fedorenko 2023-03-11  275   * @module: reference to registering module
55b810a5137d33 Vadim Fedorenko 2023-03-11  276   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  277   * Allocates memory and initialize dpll device, hold its reference on global
55b810a5137d33 Vadim Fedorenko 2023-03-11  278   * xarray.
55b810a5137d33 Vadim Fedorenko 2023-03-11  279   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  280   * Return:
55b810a5137d33 Vadim Fedorenko 2023-03-11  281   * * dpll_device struct pointer if succeeded
55b810a5137d33 Vadim Fedorenko 2023-03-11  282   * * ERR_PTR(X) - failed allocation
55b810a5137d33 Vadim Fedorenko 2023-03-11  283   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  284  struct dpll_device *
55b810a5137d33 Vadim Fedorenko 2023-03-11 @285  dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)
55b810a5137d33 Vadim Fedorenko 2023-03-11  286  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  287  	struct dpll_device *dpll;
55b810a5137d33 Vadim Fedorenko 2023-03-11  288  	int ret;
55b810a5137d33 Vadim Fedorenko 2023-03-11  289  
55b810a5137d33 Vadim Fedorenko 2023-03-11  290  	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
55b810a5137d33 Vadim Fedorenko 2023-03-11  291  	if (!dpll)
55b810a5137d33 Vadim Fedorenko 2023-03-11  292  		return ERR_PTR(-ENOMEM);
55b810a5137d33 Vadim Fedorenko 2023-03-11  293  	refcount_set(&dpll->refcount, 1);
55b810a5137d33 Vadim Fedorenko 2023-03-11  294  	dpll->dev.class = &dpll_class;
55b810a5137d33 Vadim Fedorenko 2023-03-11  295  	dpll->dev_driver_id = dev_driver_id;
55b810a5137d33 Vadim Fedorenko 2023-03-11  296  	dpll->clock_id = clock_id;
55b810a5137d33 Vadim Fedorenko 2023-03-11  297  	dpll->module = module;
55b810a5137d33 Vadim Fedorenko 2023-03-11  298  	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
55b810a5137d33 Vadim Fedorenko 2023-03-11  299  		       xa_limit_16b, GFP_KERNEL);
55b810a5137d33 Vadim Fedorenko 2023-03-11  300  	if (ret) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  301  		kfree(dpll);
55b810a5137d33 Vadim Fedorenko 2023-03-11  302  		return ERR_PTR(ret);
55b810a5137d33 Vadim Fedorenko 2023-03-11  303  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  304  	xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
55b810a5137d33 Vadim Fedorenko 2023-03-11  305  
55b810a5137d33 Vadim Fedorenko 2023-03-11  306  	return dpll;
55b810a5137d33 Vadim Fedorenko 2023-03-11  307  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  308  
55b810a5137d33 Vadim Fedorenko 2023-03-11  309  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  310   * dpll_device_get - find existing or create new dpll device
55b810a5137d33 Vadim Fedorenko 2023-03-11  311   * @clock_id: clock_id of creator
55b810a5137d33 Vadim Fedorenko 2023-03-11  312   * @dev_driver_id: id given by dev driver
55b810a5137d33 Vadim Fedorenko 2023-03-11  313   * @module: reference to registering module
55b810a5137d33 Vadim Fedorenko 2023-03-11  314   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  315   * Get existing object of a dpll device, unique for given arguments.
55b810a5137d33 Vadim Fedorenko 2023-03-11  316   * Create new if doesn't exist yet.
55b810a5137d33 Vadim Fedorenko 2023-03-11  317   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  318   * Return:
55b810a5137d33 Vadim Fedorenko 2023-03-11  319   * * valid dpll_device struct pointer if succeeded
55b810a5137d33 Vadim Fedorenko 2023-03-11  320   * * ERR_PTR of an error
55b810a5137d33 Vadim Fedorenko 2023-03-11  321   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  322  struct dpll_device *
55b810a5137d33 Vadim Fedorenko 2023-03-11  323  dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module)
55b810a5137d33 Vadim Fedorenko 2023-03-11  324  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  325  	struct dpll_device *dpll, *ret = NULL;
55b810a5137d33 Vadim Fedorenko 2023-03-11  326  	unsigned long index;
55b810a5137d33 Vadim Fedorenko 2023-03-11  327  
55b810a5137d33 Vadim Fedorenko 2023-03-11  328  	mutex_lock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  329  	xa_for_each(&dpll_device_xa, index, dpll) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  330  		if (dpll->clock_id == clock_id &&
55b810a5137d33 Vadim Fedorenko 2023-03-11  331  		    dpll->dev_driver_id == dev_driver_id &&
55b810a5137d33 Vadim Fedorenko 2023-03-11  332  		    dpll->module == module) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  333  			ret = dpll;
55b810a5137d33 Vadim Fedorenko 2023-03-11  334  			refcount_inc(&ret->refcount);
55b810a5137d33 Vadim Fedorenko 2023-03-11  335  			break;
55b810a5137d33 Vadim Fedorenko 2023-03-11  336  		}
55b810a5137d33 Vadim Fedorenko 2023-03-11  337  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  338  	if (!ret)
55b810a5137d33 Vadim Fedorenko 2023-03-11  339  		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
55b810a5137d33 Vadim Fedorenko 2023-03-11  340  	mutex_unlock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  341  
55b810a5137d33 Vadim Fedorenko 2023-03-11  342  	return ret;
55b810a5137d33 Vadim Fedorenko 2023-03-11  343  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  344  EXPORT_SYMBOL_GPL(dpll_device_get);
55b810a5137d33 Vadim Fedorenko 2023-03-11  345  
55b810a5137d33 Vadim Fedorenko 2023-03-11  346  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  347   * dpll_device_put - decrease the refcount and free memory if possible
55b810a5137d33 Vadim Fedorenko 2023-03-11  348   * @dpll: dpll_device struct pointer
55b810a5137d33 Vadim Fedorenko 2023-03-11  349   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  350   * Drop reference for a dpll device, if all references are gone, delete
55b810a5137d33 Vadim Fedorenko 2023-03-11  351   * dpll device object.
55b810a5137d33 Vadim Fedorenko 2023-03-11  352   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  353  void dpll_device_put(struct dpll_device *dpll)
55b810a5137d33 Vadim Fedorenko 2023-03-11  354  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  355  	if (!dpll)
55b810a5137d33 Vadim Fedorenko 2023-03-11  356  		return;
55b810a5137d33 Vadim Fedorenko 2023-03-11  357  	mutex_lock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  358  	if (refcount_dec_and_test(&dpll->refcount)) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  359  		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
55b810a5137d33 Vadim Fedorenko 2023-03-11  360  		xa_destroy(&dpll->pin_refs);
55b810a5137d33 Vadim Fedorenko 2023-03-11  361  		xa_erase(&dpll_device_xa, dpll->id);
55b810a5137d33 Vadim Fedorenko 2023-03-11  362  		kfree(dpll);
55b810a5137d33 Vadim Fedorenko 2023-03-11  363  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  364  	mutex_unlock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  365  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  366  EXPORT_SYMBOL_GPL(dpll_device_put);
55b810a5137d33 Vadim Fedorenko 2023-03-11  367  
55b810a5137d33 Vadim Fedorenko 2023-03-11  368  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  369   * dpll_device_register - register the dpll device in the subsystem
55b810a5137d33 Vadim Fedorenko 2023-03-11  370   * @dpll: pointer to a dpll
55b810a5137d33 Vadim Fedorenko 2023-03-11  371   * @type: type of a dpll
55b810a5137d33 Vadim Fedorenko 2023-03-11  372   * @ops: ops for a dpll device
55b810a5137d33 Vadim Fedorenko 2023-03-11  373   * @priv: pointer to private information of owner
55b810a5137d33 Vadim Fedorenko 2023-03-11  374   * @owner: pointer to owner device
55b810a5137d33 Vadim Fedorenko 2023-03-11  375   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  376   * Make dpll device available for user space.
55b810a5137d33 Vadim Fedorenko 2023-03-11  377   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  378   * Return:
55b810a5137d33 Vadim Fedorenko 2023-03-11  379   * * 0 on success
55b810a5137d33 Vadim Fedorenko 2023-03-11  380   * * -EINVAL on failure
55b810a5137d33 Vadim Fedorenko 2023-03-11  381   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  382  int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
55b810a5137d33 Vadim Fedorenko 2023-03-11  383  			 struct dpll_device_ops *ops, void *priv,
55b810a5137d33 Vadim Fedorenko 2023-03-11  384  			 struct device *owner)
55b810a5137d33 Vadim Fedorenko 2023-03-11  385  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  386  	if (WARN_ON(!ops || !owner))
55b810a5137d33 Vadim Fedorenko 2023-03-11  387  		return -EINVAL;
55b810a5137d33 Vadim Fedorenko 2023-03-11  388  	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
55b810a5137d33 Vadim Fedorenko 2023-03-11  389  		return -EINVAL;
55b810a5137d33 Vadim Fedorenko 2023-03-11  390  	mutex_lock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  391  	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  392  		mutex_unlock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  393  		return -EEXIST;
55b810a5137d33 Vadim Fedorenko 2023-03-11  394  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  395  	dpll->dev.bus = owner->bus;
55b810a5137d33 Vadim Fedorenko 2023-03-11  396  	dpll->parent = owner;
55b810a5137d33 Vadim Fedorenko 2023-03-11  397  	dpll->type = type;
55b810a5137d33 Vadim Fedorenko 2023-03-11  398  	dpll->ops = ops;
55b810a5137d33 Vadim Fedorenko 2023-03-11  399  	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
55b810a5137d33 Vadim Fedorenko 2023-03-11  400  		     dpll->dev_driver_id);
55b810a5137d33 Vadim Fedorenko 2023-03-11  401  	dpll->priv = priv;
55b810a5137d33 Vadim Fedorenko 2023-03-11  402  	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
55b810a5137d33 Vadim Fedorenko 2023-03-11  403  	mutex_unlock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  404  	dpll_notify_device_create(dpll);
55b810a5137d33 Vadim Fedorenko 2023-03-11  405  
55b810a5137d33 Vadim Fedorenko 2023-03-11  406  	return 0;
55b810a5137d33 Vadim Fedorenko 2023-03-11  407  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  408  EXPORT_SYMBOL_GPL(dpll_device_register);
55b810a5137d33 Vadim Fedorenko 2023-03-11  409  
55b810a5137d33 Vadim Fedorenko 2023-03-11  410  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  411   * dpll_device_unregister - deregister dpll device
55b810a5137d33 Vadim Fedorenko 2023-03-11  412   * @dpll: registered dpll pointer
55b810a5137d33 Vadim Fedorenko 2023-03-11  413   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  414   * Deregister device, make it unavailable for userspace.
55b810a5137d33 Vadim Fedorenko 2023-03-11  415   * Note: It does not free the memory
55b810a5137d33 Vadim Fedorenko 2023-03-11  416   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  417  void dpll_device_unregister(struct dpll_device *dpll)
55b810a5137d33 Vadim Fedorenko 2023-03-11  418  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  419  	mutex_lock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  420  	ASSERT_DPLL_REGISTERED(dpll);
55b810a5137d33 Vadim Fedorenko 2023-03-11  421  	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
55b810a5137d33 Vadim Fedorenko 2023-03-11  422  	mutex_unlock(&dpll_device_xa_lock);
55b810a5137d33 Vadim Fedorenko 2023-03-11  423  	dpll_notify_device_delete(dpll);
55b810a5137d33 Vadim Fedorenko 2023-03-11  424  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  425  EXPORT_SYMBOL_GPL(dpll_device_unregister);
55b810a5137d33 Vadim Fedorenko 2023-03-11  426  
55b810a5137d33 Vadim Fedorenko 2023-03-11  427  /**
55b810a5137d33 Vadim Fedorenko 2023-03-11  428   * dpll_pin_alloc - allocate the memory for dpll pin
55b810a5137d33 Vadim Fedorenko 2023-03-11  429   * @clock_id: clock_id of creator
55b810a5137d33 Vadim Fedorenko 2023-03-11  430   * @dev_driver_id: id given by dev driver
55b810a5137d33 Vadim Fedorenko 2023-03-11  431   * @module: reference to registering module
55b810a5137d33 Vadim Fedorenko 2023-03-11  432   * @prop: dpll pin properties
55b810a5137d33 Vadim Fedorenko 2023-03-11  433   *
55b810a5137d33 Vadim Fedorenko 2023-03-11  434   * Return:
55b810a5137d33 Vadim Fedorenko 2023-03-11  435   * * valid allocated dpll_pin struct pointer if succeeded
55b810a5137d33 Vadim Fedorenko 2023-03-11  436   * * ERR_PTR of an error
55b810a5137d33 Vadim Fedorenko 2023-03-11  437   */
55b810a5137d33 Vadim Fedorenko 2023-03-11  438  struct dpll_pin *
55b810a5137d33 Vadim Fedorenko 2023-03-11 @439  dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
55b810a5137d33 Vadim Fedorenko 2023-03-11  440  	       const struct dpll_pin_properties *prop)
55b810a5137d33 Vadim Fedorenko 2023-03-11  441  {
55b810a5137d33 Vadim Fedorenko 2023-03-11  442  	struct dpll_pin *pin;
55b810a5137d33 Vadim Fedorenko 2023-03-11  443  	int ret;
55b810a5137d33 Vadim Fedorenko 2023-03-11  444  
55b810a5137d33 Vadim Fedorenko 2023-03-11  445  	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
55b810a5137d33 Vadim Fedorenko 2023-03-11  446  	if (!pin)
55b810a5137d33 Vadim Fedorenko 2023-03-11  447  		return ERR_PTR(-ENOMEM);
55b810a5137d33 Vadim Fedorenko 2023-03-11  448  	pin->dev_driver_id = device_drv_id;
55b810a5137d33 Vadim Fedorenko 2023-03-11  449  	pin->clock_id = clock_id;
55b810a5137d33 Vadim Fedorenko 2023-03-11  450  	pin->module = module;
55b810a5137d33 Vadim Fedorenko 2023-03-11  451  	refcount_set(&pin->refcount, 1);
55b810a5137d33 Vadim Fedorenko 2023-03-11  452  	if (WARN_ON(!prop->description)) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  453  		ret = -EINVAL;
55b810a5137d33 Vadim Fedorenko 2023-03-11  454  		goto release;
55b810a5137d33 Vadim Fedorenko 2023-03-11  455  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  456  	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
55b810a5137d33 Vadim Fedorenko 2023-03-11  457  	if (!pin->prop.description) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  458  		ret = -ENOMEM;
55b810a5137d33 Vadim Fedorenko 2023-03-11  459  		goto release;
55b810a5137d33 Vadim Fedorenko 2023-03-11  460  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  461  	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
55b810a5137d33 Vadim Fedorenko 2023-03-11  462  		    prop->type > DPLL_PIN_TYPE_MAX)) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  463  		ret = -EINVAL;
55b810a5137d33 Vadim Fedorenko 2023-03-11  464  		goto release;
55b810a5137d33 Vadim Fedorenko 2023-03-11  465  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  466  	pin->prop.type = prop->type;
55b810a5137d33 Vadim Fedorenko 2023-03-11  467  	pin->prop.capabilities = prop->capabilities;
55b810a5137d33 Vadim Fedorenko 2023-03-11  468  	pin->prop.freq_supported = prop->freq_supported;
55b810a5137d33 Vadim Fedorenko 2023-03-11  469  	pin->prop.any_freq_min = prop->any_freq_min;
55b810a5137d33 Vadim Fedorenko 2023-03-11  470  	pin->prop.any_freq_max = prop->any_freq_max;
55b810a5137d33 Vadim Fedorenko 2023-03-11  471  	xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
55b810a5137d33 Vadim Fedorenko 2023-03-11  472  	xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
55b810a5137d33 Vadim Fedorenko 2023-03-11  473  	ret = xa_alloc(&dpll_pin_xa, &pin->idx, pin,
55b810a5137d33 Vadim Fedorenko 2023-03-11  474  		       xa_limit_16b, GFP_KERNEL);
55b810a5137d33 Vadim Fedorenko 2023-03-11  475  release:
55b810a5137d33 Vadim Fedorenko 2023-03-11  476  	if (ret) {
55b810a5137d33 Vadim Fedorenko 2023-03-11  477  		xa_destroy(&pin->dpll_refs);
55b810a5137d33 Vadim Fedorenko 2023-03-11  478  		xa_destroy(&pin->parent_refs);
55b810a5137d33 Vadim Fedorenko 2023-03-11  479  		kfree(pin->prop.description);
55b810a5137d33 Vadim Fedorenko 2023-03-11  480  		kfree(pin->rclk_dev_name);
55b810a5137d33 Vadim Fedorenko 2023-03-11  481  		kfree(pin);
55b810a5137d33 Vadim Fedorenko 2023-03-11  482  		return ERR_PTR(ret);
55b810a5137d33 Vadim Fedorenko 2023-03-11  483  	}
55b810a5137d33 Vadim Fedorenko 2023-03-11  484  
55b810a5137d33 Vadim Fedorenko 2023-03-11  485  	return pin;
55b810a5137d33 Vadim Fedorenko 2023-03-11  486  }
55b810a5137d33 Vadim Fedorenko 2023-03-11  487  

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

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

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

Hi Vadim,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on linus/master]
[also build test ERROR on v6.3-rc1 next-20230310]
[cannot apply to tnguy-next-queue/dev-queue]
[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/20230312-103056
patch link:    https://lore.kernel.org/r/20230312022807.278528-6-vadfed%40meta.com
patch subject: [PATCH RFC v6 5/6] ice: implement dpll interface to control cgu
config: i386-randconfig-a013 (https://download.01.org/0day-ci/archive/20230312/202303121331.swxKROtl-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/c4acb622eb0fd3120fc9cdcaf33fddfa7da04a50
        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/20230312-103056
        git checkout c4acb622eb0fd3120fc9cdcaf33fddfa7da04a50
        # 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/202303121331.swxKROtl-lkp@intel.com/

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

>> drivers/net/ethernet/intel/ice/ice_dpll.c:155: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:161:18: note: uninitialized use occurs here
           for (i = 0; i < pin_num; i++)
                           ^~~~~~~
   drivers/net/ethernet/intel/ice/ice_dpll.c:155: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:143:13: note: initialize the variable 'pin_num' to silence this warning
           int pin_num, i;
                      ^
                       = 0
>> drivers/net/ethernet/intel/ice/ice_dpll.c:412: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:416:6: note: uninitialized use occurs here
           if (ret)
               ^~~
   drivers/net/ethernet/intel/ice/ice_dpll.c:412: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:407:9: note: initialize the variable 'ret' to silence this warning
           int ret;
                  ^
                   = 0
   drivers/net/ethernet/intel/ice/ice_dpll.c:449: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:453:6: note: uninitialized use occurs here
           if (ret)
               ^~~
   drivers/net/ethernet/intel/ice/ice_dpll.c:449: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:444:9: note: initialize the variable 'ret' to silence this warning
           int ret;
                  ^
                   = 0
>> drivers/net/ethernet/intel/ice/ice_dpll.c:478: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:477: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:499: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:519:9: note: uninitialized use occurs here
           return ret;
                  ^~~
   drivers/net/ethernet/intel/ice/ice_dpll.c:499: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:481:9: note: initialize the variable 'ret' to silence this warning
           int ret;
                  ^
                   = 0
   5 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_mask" [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] 202+ messages in thread

* Re: [PATCH RFC v6 4/6] ice: add admin commands to access cgu configuration
  2023-03-12  2:28   ` Vadim Fedorenko
  (?)
@ 2023-03-12  7:05   ` kernel test robot
  -1 siblings, 0 replies; 202+ messages in thread
From: kernel test robot @ 2023-03-12  7:05 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

Hi Vadim,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on linus/master]
[also build test ERROR on v6.3-rc1 next-20230310]
[cannot apply to tnguy-next-queue/dev-queue]
[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/20230312-103056
patch link:    https://lore.kernel.org/r/20230312022807.278528-5-vadfed%40meta.com
patch subject: [PATCH RFC v6 4/6] ice: add admin commands to access cgu configuration
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20230312/202303121450.oxVbU44L-lkp@intel.com/config)
compiler: alpha-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/0b8a02c116e829e4a4f8dd4a3d584b47688fed76
        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/20230312-103056
        git checkout 0b8a02c116e829e4a4f8dd4a3d584b47688fed76
        # 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=alpha olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=alpha 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/202303121450.oxVbU44L-lkp@intel.com/

All errors (new ones prefixed by >>):

   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:
   drivers/net/ethernet/intel/ice/ice_ptp_hw.h: 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_F_PHY_RCLK +4334 drivers/net/ethernet/intel/ice/ice_lib.c

  4315	
  4316	/**
  4317	 * ice_init_feature_support
  4318	 * @pf: pointer to the struct ice_pf instance
  4319	 *
  4320	 * called during init to setup supported feature
  4321	 */
  4322	void ice_init_feature_support(struct ice_pf *pf)
  4323	{
  4324		switch (pf->hw.device_id) {
  4325		case ICE_DEV_ID_E810C_BACKPLANE:
  4326		case ICE_DEV_ID_E810C_QSFP:
  4327		case ICE_DEV_ID_E810C_SFP:
  4328		case ICE_DEV_ID_E810_XXV_BACKPLANE:
  4329		case ICE_DEV_ID_E810_XXV_QSFP:
  4330		case ICE_DEV_ID_E810_XXV_SFP:
  4331			ice_set_feature_support(pf, ICE_F_DSCP);
  4332			ice_set_feature_support(pf, ICE_F_PTP_EXTTS);
  4333			if (ice_is_phy_rclk_present(&pf->hw))
> 4334				ice_set_feature_support(pf, ICE_F_PHY_RCLK);
  4335			/* If we don't own the timer - don't enable other caps */
  4336			if (!pf->hw.func_caps.ts_func_info.src_tmr_owned)
  4337				break;
  4338			if (ice_is_cgu_present(&pf->hw))
  4339				ice_set_feature_support(pf, ICE_F_CGU);
  4340			if (ice_is_clock_mux_present_e810t(&pf->hw))
  4341				ice_set_feature_support(pf, ICE_F_SMA_CTRL);
  4342			if (ice_gnss_is_gps_present(&pf->hw))
  4343				ice_set_feature_support(pf, ICE_F_GNSS);
  4344			break;
  4345		default:
  4346			break;
  4347		}
  4348	}
  4349	

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

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-13 12:20   ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-13 12:20 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and
>make it flexible and easy to cover special configurations.

Could you please put here some command line examples to work with this?


>
>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
>
>Vadim Fedorenko (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/netlink/specs/dpll.yaml         |  514 +++++
> Documentation/networking/dpll.rst             |  347 ++++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    9 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |   10 +
> drivers/dpll/dpll_core.c                      |  835 ++++++++
> drivers/dpll/dpll_core.h                      |   99 +
> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
> drivers/dpll/dpll_netlink.h                   |   30 +
> 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     | 1845 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
> 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   |  411 ++++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
> drivers/ptp/Kconfig                           |    1 +
> drivers/ptp/ptp_ocp.c                         |  206 +-
> include/linux/dpll.h                          |  284 +++
> include/uapi/linux/dpll.h                     |  196 ++
> 31 files changed, 7135 insertions(+), 16 deletions(-)
> create mode 100644 Documentation/netlink/specs/dpll.yaml
> create mode 100644 Documentation/networking/dpll.rst
> 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 include/linux/dpll.h
> create mode 100644 include/uapi/linux/dpll.h
>
>-- 
>2.34.1
>

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-13 12:20   ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-13 12:20 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and
>make it flexible and easy to cover special configurations.

Could you please put here some command line examples to work with this?


>
>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
>
>Vadim Fedorenko (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/netlink/specs/dpll.yaml         |  514 +++++
> Documentation/networking/dpll.rst             |  347 ++++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    9 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |   10 +
> drivers/dpll/dpll_core.c                      |  835 ++++++++
> drivers/dpll/dpll_core.h                      |   99 +
> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
> drivers/dpll/dpll_netlink.h                   |   30 +
> 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     | 1845 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
> 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   |  411 ++++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
> drivers/ptp/Kconfig                           |    1 +
> drivers/ptp/ptp_ocp.c                         |  206 +-
> include/linux/dpll.h                          |  284 +++
> include/uapi/linux/dpll.h                     |  196 ++
> 31 files changed, 7135 insertions(+), 16 deletions(-)
> create mode 100644 Documentation/netlink/specs/dpll.yaml
> create mode 100644 Documentation/networking/dpll.rst
> 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 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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-13 12:20   ` Jiri Pirko
@ 2023-03-13 15:33     ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-13 15:33 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 13/03/2023 12:20, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> Implement common API for clock/DPLL configuration and status reporting.
>> The API utilises netlink interface as transport for commands and event
>> notifications. This API aim to extend current pin configuration and
>> make it flexible and easy to cover special configurations.
> 
> Could you please put here some command line examples to work with this?

We don't have open-source tools ready right now for specific hardware, 
but with YAML spec published you can use in-kernel tool to manipulate 
the values, i.e.:

./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--dump device-get
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
device-get --json '{"id": 0}'
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--dump pin-get
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
pin-get --json '{"id": 0, "pin-idx":1}'
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

> 
>>
>> 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
>>
>> Vadim Fedorenko (3):
>>   dpll: Add DPLL framework base functions
>>   dpll: documentation on DPLL subsystem interface
>>   ptp_ocp: implement DPLL ops
>>
>> Documentation/netlink/specs/dpll.yaml         |  514 +++++
>> Documentation/networking/dpll.rst             |  347 ++++
>> Documentation/networking/index.rst            |    1 +
>> MAINTAINERS                                   |    9 +
>> drivers/Kconfig                               |    2 +
>> drivers/Makefile                              |    1 +
>> drivers/dpll/Kconfig                          |    7 +
>> drivers/dpll/Makefile                         |   10 +
>> drivers/dpll/dpll_core.c                      |  835 ++++++++
>> drivers/dpll/dpll_core.h                      |   99 +
>> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>> drivers/dpll/dpll_netlink.h                   |   30 +
>> 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     | 1845 +++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>> 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   |  411 ++++
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>> drivers/ptp/Kconfig                           |    1 +
>> drivers/ptp/ptp_ocp.c                         |  206 +-
>> include/linux/dpll.h                          |  284 +++
>> include/uapi/linux/dpll.h                     |  196 ++
>> 31 files changed, 7135 insertions(+), 16 deletions(-)
>> create mode 100644 Documentation/netlink/specs/dpll.yaml
>> create mode 100644 Documentation/networking/dpll.rst
>> 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 include/linux/dpll.h
>> create mode 100644 include/uapi/linux/dpll.h
>>
>> -- 
>> 2.34.1
>>


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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-13 15:33     ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-13 15:33 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 13/03/2023 12:20, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> Implement common API for clock/DPLL configuration and status reporting.
>> The API utilises netlink interface as transport for commands and event
>> notifications. This API aim to extend current pin configuration and
>> make it flexible and easy to cover special configurations.
> 
> Could you please put here some command line examples to work with this?

We don't have open-source tools ready right now for specific hardware, 
but with YAML spec published you can use in-kernel tool to manipulate 
the values, i.e.:

./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--dump device-get
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
device-get --json '{"id": 0}'
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--dump pin-get
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
pin-get --json '{"id": 0, "pin-idx":1}'
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do 
pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

> 
>>
>> 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
>>
>> Vadim Fedorenko (3):
>>   dpll: Add DPLL framework base functions
>>   dpll: documentation on DPLL subsystem interface
>>   ptp_ocp: implement DPLL ops
>>
>> Documentation/netlink/specs/dpll.yaml         |  514 +++++
>> Documentation/networking/dpll.rst             |  347 ++++
>> Documentation/networking/index.rst            |    1 +
>> MAINTAINERS                                   |    9 +
>> drivers/Kconfig                               |    2 +
>> drivers/Makefile                              |    1 +
>> drivers/dpll/Kconfig                          |    7 +
>> drivers/dpll/Makefile                         |   10 +
>> drivers/dpll/dpll_core.c                      |  835 ++++++++
>> drivers/dpll/dpll_core.h                      |   99 +
>> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>> drivers/dpll/dpll_netlink.h                   |   30 +
>> 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     | 1845 +++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>> 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   |  411 ++++
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>> drivers/ptp/Kconfig                           |    1 +
>> drivers/ptp/ptp_ocp.c                         |  206 +-
>> include/linux/dpll.h                          |  284 +++
>> include/uapi/linux/dpll.h                     |  196 ++
>> 31 files changed, 7135 insertions(+), 16 deletions(-)
>> create mode 100644 Documentation/netlink/specs/dpll.yaml
>> create mode 100644 Documentation/networking/dpll.rst
>> 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 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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>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.
>
>Changes:
>dpll: adapt changes after introduction of dpll yaml spec
>dpll: redesign after review comments, fix minor issues
>dpll: add get pin command
>dpll: _get/_put approach for creating and realesing pin or dpll objects
>dpll: lock access to dplls with global lock
>dpll: lock access to pins with global lock
>
>dpll: replace cookie with clock id
>dpll: add clock class

Looks like some leftover in patch description.


>
>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: follow one naming schema in dpll subsys
>
>dpll: fix dpll device naming scheme
>
>Fix dpll device naming scheme by use of 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.
>
>dpll: remove description length parameter
>
>dpll: fix 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.
>
>dpll: move function comments to dpll_core.c, fix exports
>dpll: remove single-use helper functions
>dpll: merge device register with alloc
>dpll: lock and unlock mutex on dpll device release
>dpll: move dpll_type to uapi header
>dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>dpll: rename dpll_pin_state to dpll_pin_mode
>dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>dpll: remove DPLL_CHANGE_PIN_TYPE enum value

I'm confused, some messed-up squash?


>
>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                 |    9 +
> drivers/Kconfig             |    2 +
> drivers/Makefile            |    1 +
> drivers/dpll/Kconfig        |    7 +
> drivers/dpll/Makefile       |   10 +
> drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    |   99 ++++
> drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |   30 +
> include/linux/dpll.h        |  284 ++++++++++
> 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
>+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,10 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL)          += dpll_sys.o

What's "sys" and why is it here?


>+dpll_sys-y                  += dpll_core.o
>+dpll_sys-y                  += dpll_netlink.o
>+dpll_sys-y                  += dpll_nl.o
>+
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..3fc151e16751
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,835 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.
>+ *
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates

IIUC, there's a lot of Intel work behind this, I think some credits
should go here as well.

Also, it is 2023, you live in the past :)


>+ */
>+
>+#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_device_xa_lock);
>+DEFINE_MUTEX(dpll_pin_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))
>+
>+static struct class dpll_class = {
>+	.name = "dpll",
>+};
>+
>+/**
>+ * 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)
>+{
>+	struct dpll_device *dpll = NULL;
>+
>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>+		dpll = xa_load(&dpll_device_xa, id);
		return xa_load

>+
	return NULL

>+	return dpll;

Anyway, I believe this fuction should go away, see below.


>+}
>+
>+/**
>+ * 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 index;
>+
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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 or increase refcount on existing pin
>+ * reference on given xarray.
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -ENOMEM on failed allocation
>+ */
>+static int
>+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;

In previous function you use "index" name for the same variable. Be
consistent. I think "i" is actually better.


>+	u32 idx;
>+	int ret;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			refcount_inc(&ref->refcount);
>+			return 0;
>+		}
>+	}
>+
>+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->pin = pin;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		refcount_set(&ref->refcount, 1);
>+	else
>+		kfree(ref);
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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 references are dropped, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_pins, i);
>+				kfree(ref);
>+			}
>+			return 0;
>+		}
>+	}
>+
>+	return -EINVAL;
>+}
>+
>+/**
>+ * dpll_xa_ref_pin_find - find pin reference on xarray
>+ * @xa_pins: dpll_pin_ref xarray holding pins
>+ * @pin: pointer to a pin
>+ *
>+ * Search for pin reference struct of a given pin 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_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin)
>+			return ref;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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,
>+		     struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 idx;
>+	int ret;
>+
>+	xa_for_each(xa_dplls, i, ref) {
>+		if (ref->dpll == dpll) {
>+			refcount_inc(&ref->refcount);
>+			return 0;
>+		}
>+	}
>+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->dpll = dpll;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		refcount_set(&ref->refcount, 1);
>+	else
>+		kfree(ref);
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_dplls, i, ref) {
>+		if (ref->dpll == dpll) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_dplls, i);
>+				kfree(ref);
>+			}
>+			break;
>+		}
>+	}
>+}
>+
>+/**
>+ * 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;
>+}
>+
>+
>+/**
>+ * dpll_device_alloc - allocate the memory for dpll device
>+ * @clock_id: clock_id of creator
>+ * @dev_driver_id: 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
>+ */
>+struct dpll_device *
>+dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)

This should be static. Didn't you get warn?


>+{
>+	struct dpll_device *dpll;
>+	int ret;
>+
>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>+	if (!dpll)
>+		return ERR_PTR(-ENOMEM);
>+	refcount_set(&dpll->refcount, 1);
>+	dpll->dev.class = &dpll_class;
>+	dpll->dev_driver_id = dev_driver_id;
>+	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
>+ * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each(&dpll_device_xa, index, dpll) {
>+		if (dpll->clock_id == clock_id &&
>+		    dpll->dev_driver_id == dev_driver_id &&

Why you need "dev_driver_id"? clock_id is here for the purpose of
identification, isn't that enough for you.

Plus, the name is odd. "dev_driver" should certainly be avoided.


>+		    dpll->module == module) {
>+			ret = dpll;
>+			refcount_inc(&ret->refcount);
>+			break;
>+		}
>+	}
>+	if (!ret)
>+		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>+	mutex_unlock(&dpll_device_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;

Remove this check. The driver should not call this with NULL.


>+	mutex_lock(&dpll_device_xa_lock);
>+	if (refcount_dec_and_test(&dpll->refcount)) {
>+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));

ASSERT_DPLL_NOT_REGISTERED(dpll);


>+		xa_destroy(&dpll->pin_refs);
>+		xa_erase(&dpll_device_xa, dpll->id);
>+		kfree(dpll);
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_put);
>+
>+/**
>+ * 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,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)
>+{
>+	if (WARN_ON(!ops || !owner))
>+		return -EINVAL;
>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>+		return -EINVAL;
>+	mutex_lock(&dpll_device_xa_lock);
>+	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>+		mutex_unlock(&dpll_device_xa_lock);
>+		return -EEXIST;
>+	}
>+	dpll->dev.bus = owner->bus;
>+	dpll->parent = owner;
>+	dpll->type = type;
>+	dpll->ops = ops;
>+	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>+		     dpll->dev_driver_id);

This is really odd. As a result, the user would see something like:
pci/0000:01:00.0_1
pci/0000:01:00.0_2

I have to say it is confusing. In devlink, is bus/name and the user
could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
there. Also, "_" might have some meaning on some bus. Should not
concatename dev_name() with anything.

Thinking about this some more, the module/clock_id tuple should be
uniqueue and stable. It is used for dpll_device_get(), it could be used
as the user handle, can't it?
Example:
ice/c92d02a7129f4747
mlx5/90265d8bf6e6df56

If you really need the "dev_driver_id" (as I believe clock_id should be
enough), you can put it here as well:
ice/c92d02a7129f4747/1
ice/c92d02a7129f4747/2

This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
share instance of DPLL equally, there is no "one clock master".



>+	dpll->priv = priv;
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_device_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
>+ *
>+ * Deregister device, make it unavailable for userspace.
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll)
>+{
>+	mutex_lock(&dpll_device_xa_lock);
>+	ASSERT_DPLL_REGISTERED(dpll);
>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_device_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
>+ * @dev_driver_id: id 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

Extra "*"'s


>+ */
>+struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,

Odd whitespace.

Also, func should be static.


>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	pin->dev_driver_id = device_drv_id;

Name inconsistency: driver/drv
you have it on multiple places


>+	pin->clock_id = clock_id;
>+	pin->module = module;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->description)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>+	if (!pin->prop.description) {
>+		ret = -ENOMEM;
>+		goto release;
>+	}
>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.type = prop->type;
>+	pin->prop.capabilities = prop->capabilities;
>+	pin->prop.freq_supported = prop->freq_supported;
>+	pin->prop.any_freq_min = prop->any_freq_min;
>+	pin->prop.any_freq_max = prop->any_freq_max;

Make sure that the driver maintains prop (static const) and just save
the pointer. Prop does not need to be something driver needs to change.


>+	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->idx, pin,
>+		       xa_limit_16b, GFP_KERNEL);
>+release:
>+	if (ret) {
>+		xa_destroy(&pin->dpll_refs);
>+		xa_destroy(&pin->parent_refs);
>+		kfree(pin->prop.description);
>+		kfree(pin->rclk_dev_name);
>+		kfree(pin);
>+		return ERR_PTR(ret);
>+	}
>+
>+	return pin;
>+}
>+
>+/**
>+ * dpll_pin_get - find existing or create new dpll pin
>+ * @clock_id: clock_id of creator
>+ * @dev_driver_id: id 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

This is one example, I'm pretty sure that there are others, when you
have text inconsistencies in func doc for the same function in .c and .h
Have it please only on one place. .c is the usual place.


>+ */
>+struct dpll_pin *
>+dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,

Again, why do you need this device_drv_id? Clock id should be enough.


>+	     const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pos, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_pin_xa_lock);
>+	xa_for_each(&dpll_pin_xa, index, pos) {
>+		if (pos->clock_id == clock_id &&
>+		    pos->dev_driver_id == device_drv_id &&
>+		    pos->module == module) {

Compare prop as well.

Can't the driver_id (pin index) be something const as well? I think it
should. And therefore it could be easily put inside.


>+			ret = pos;
>+			refcount_inc(&ret->refcount);
>+			break;
>+		}
>+	}
>+	if (!ret)
>+		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	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;
>+	mutex_lock(&dpll_pin_xa_lock);
>+	if (refcount_dec_and_test(&pin->refcount)) {
>+		xa_destroy(&pin->dpll_refs);
>+		xa_destroy(&pin->parent_refs);
>+		xa_erase(&dpll_pin_xa, pin->idx);
>+		kfree(pin->prop.description);
>+		kfree(pin->rclk_dev_name);
>+		kfree(pin);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_put);
>+
>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	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);
>+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,
>+		  struct dpll_pin_ops *ops, void *priv,
>+		  struct device *rclk_device)

Wait a second, what is this "struct device *"? Looks very odd.


>+{
>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;

If you need to store something here, store the pointer to the device
directly. But this rclk_device seems odd to me.
Dev_name is in case of PCI device for example 0000:01:00.0? That alone
is incomplete. What should it server for?



>+	int ret;
>+
>+	if (WARN_ON(!dpll))
>+		return -EINVAL;
>+	if (WARN_ON(!pin))
>+		return -EINVAL;

Remove these checks and other similar checks in the code. It should rely
on basic driver sanity.


>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+static void
>+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
>+}
>+
>+/**
>+ * dpll_pin_unregister - deregister dpll pin from dpll device
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * Note: It does not free the memory
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>+		return -ENOENT;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	__dpll_pin_unregister(dpll, pin);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+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,
>+			 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(!pin || !parent))
>+		return -EINVAL;
>+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>+		return -EPERM;

I don't think that EPERM is suitable for this. Just use EINVAL. The
driver is buggy in this case anyway.


>+	mutex_lock(&dpll_pin_xa_lock);
>+	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_device_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_device_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_device_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin);
>+			mutex_unlock(&dpll_device_xa_lock);
>+		}
>+	}
>+	refcount_dec(&pin->refcount);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+unlock:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	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
>+ *
>+ * Note: It does not free the memory
>+ */
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+	refcount_dec(&pin->refcount);
>+	xa_for_each(&pin->dpll_refs, i, ref) {
>+		__dpll_pin_unregister(ref->dpll, pin);
>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>+				       DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>+
>+/**
>+ * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>+ * @dpll: dpll device pointer
>+ * @idx: index of pin
>+ *
>+ * Find a reference to a pin registered with given dpll and return its pointer.
>+ *
>+ * Return:
>+ * * valid pointer if pin was found
>+ * * NULL if not found
>+ */
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>+{
>+	struct dpll_pin_ref *pos;
>+	unsigned long i;
>+
>+	xa_for_each(&dpll->pin_refs, i, pos) {
>+		if (pos && pos->pin && pos->pin->dev_driver_id == idx)

How exactly pos->pin could be NULL?

Also, you are degrading the xarray to a mere list here with lookup like
this. Why can't you use the pin index coming from driver and
insert/lookup based on this index?


>+			return pos->pin;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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)
>+{
>+	return dpll->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_priv);
>+
>+/**
>+ * 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,

IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
Just pass the priv directly to the op and avoid need for this helper,
no? Same goes to the rest of the priv helpers.


>+			    const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);

Why cast is needed here? You have this on multiple places.


>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
>+
>+	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	ret = class_register(&dpll_class);

Why exactly do you need this? I asked to remove this previously, IIRC
you said you would check if this needed. Why?


>+	if (ret)
>+		goto unregister_netlink;
>+
>+	return 0;
>+
>+unregister_netlink:
>+	dpll_netlink_finish();
>+error:
>+	mutex_destroy(&dpll_device_xa_lock);
>+	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,99 @@
>+/* 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/refcount.h>
>+#include "dpll_netlink.h"
>+
>+#define DPLL_REGISTERED		XA_MARK_1
>+
>+/**
>+ * struct dpll_device - structure for a DPLL device
>+ * @id:			unique id number for each device
>+ * @dev_driver_id:	id given by dev driver
>+ * @dev:		struct device for this dpll device
>+ * @parent:		parent device
>+ * @module:		module of creator
>+ * @ops:		operations this &dpll_device supports
>+ * @lock:		mutex to serialize operations
>+ * @type:		type of a dpll
>+ * @priv:		pointer to private information of owner
>+ * @pins:		list of pointers to pins registered with this dpll
>+ * @clock_id:		unique identifier (clock_id) of a dpll
>+ * @mode_supported_mask: mask of supported modes
>+ * @refcount:		refcount
>+ **/
>+struct dpll_device {
>+	u32 id;
>+	u32 dev_driver_id;
>+	struct device dev;
>+	struct device *parent;
>+	struct module *module;
>+	struct dpll_device_ops *ops;
>+	enum dpll_type type;
>+	void *priv;
>+	struct xarray pin_refs;
>+	u64 clock_id;
>+	unsigned long mode_supported_mask;
>+	refcount_t refcount;
>+};
>+
>+/**
>+ * 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 idx;
>+	u32 dev_driver_id;
>+	u64 clock_id;
>+	struct module *module;

Have the ordering of common fields, like clock_id and module consistent
with struct dpll_device


>+	struct xarray dpll_refs;
>+	struct xarray parent_refs;
>+	struct dpll_pin_properties prop;
>+	char *rclk_dev_name;
>+	refcount_t refcount;
>+};
>+
>+/**
>+ * struct dpll_pin_ref - structure for referencing either dpll or pins
>+ * @dpll:		pointer to a dpll
>+ * @pin:		pointer to a pin
>+ * @ops:		ops for a dpll pin
>+ * @priv:		pointer to private information of owner
>+ **/
>+struct dpll_pin_ref {
>+	union {
>+		struct dpll_device *dpll;
>+		struct dpll_pin *pin;
>+	};
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+	refcount_t refcount;
>+};
>+
>+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);
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
>+struct dpll_pin_ref *
>+dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
>+struct dpll_pin_ref *
>+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
>+extern struct xarray dpll_device_xa;
>+extern struct xarray dpll_pin_xa;
>+extern struct mutex dpll_device_xa_lock;
>+extern struct mutex dpll_pin_xa_lock;
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..46aefeb1ac93
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,1065 @@
>+// 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>
>+
>+static u32 dpll_pin_freq_value[] = {
>+	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
>+	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
>+};
>+
>+static int
>+dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
>+{
>+	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))

Why exactly do we need this dua--handle scheme? Why do you need
unpredictable DPLL_A_ID to be exposed to userspace?
It's just confusing.



>+		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, const struct dpll_device *dpll,
>+		  struct netlink_ext_ack *extack)
>+{
>+	enum dpll_mode mode;
>+
>+	if (WARN_ON(!dpll->ops->mode_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->mode_get(dpll, &mode, extack))
>+		return -EFAULT;
>+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
>+			    struct netlink_ext_ack *extack)
>+{
>+	u32 source_pin_idx;
>+
>+	if (WARN_ON(!dpll->ops->source_pin_idx_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	enum dpll_lock_status status;
>+
>+	if (WARN_ON(!dpll->ops->lock_status_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->lock_status_get(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)
>+{
>+	s32 temp;
>+
>+	if (!dpll->ops->temp_get)
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->temp_get(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)
>+{
>+	u32 prio;
>+
>+	if (!ref->ops->prio_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->prio_get(pin, ref->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)
>+{
>+	enum dpll_pin_state state;
>+
>+	if (!ref->ops->state_on_dpll_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_get(pin, ref->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 netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->direction_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))
>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref_parent;
>+	enum dpll_pin_state state;
>+	struct nlattr *nest;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(&pin->parent_refs, index, ref_parent) {
>+		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
>+			return -EFAULT;
>+		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
>+							&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,
>+				ref_parent->pin->dev_driver_id)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		nla_nest_end(msg, nest);
>+	}
>+
>+	return 0;
>+
>+nest_cancel:
>+	nla_nest_cancel(msg, nest);
>+	return ret;
>+}
>+
>+static int
>+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref = NULL;

Why this needs to be initialized?


>+	enum dpll_pin_state state;
>+	struct nlattr *nest;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(&pin->parent_refs, index, ref) {
>+		if (WARN_ON(!ref->ops->state_on_pin_get))
>+			return -EFAULT;
>+		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>+				ref->pin->dev_driver_id)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		nla_nest_end(msg, nest);
>+	}

How is this function different to dpll_msg_add_pin_parents()?
Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
hard to follow for me :/

Did you get lost here as well? If yes, this needs some serious think
through :)



>+
>+	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))

Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
code.

>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		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, bool dump_dpll)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
>+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (pin->rclk_dev_name)
>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;

Lots of code duplication with the previous functions, please unify.


>+
>+	return 0;
>+}
>+
>+static int
>+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
>+		     struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	enum dpll_mode mode;
>+	unsigned long i;
>+	int ret;
>+
>+	ret = dpll_msg_add_dev_handle(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_source_pin_idx(msg, dpll, extack);
>+	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;
>+	xa_for_each(&dpll->pin_refs, i, ref) {
>+		struct nlattr *nest = nla_nest_start(msg, DPLL_A_PIN);
>+
>+		if (!nest) {
>+			ret = -EMSGSIZE;
>+			break;
>+		}
>+		ret = dpll_cmd_pin_on_dpll_get(msg, ref->pin, dpll, extack);
>+		if (ret) {
>+			nla_nest_cancel(msg, nest);
>+			break;
>+		}
>+		nla_nest_end(msg, nest);
>+	}
>+
>+	return ret;
>+}
>+
>+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+
>+	if (freq >= pin->prop.any_freq_min && freq <= pin->prop.any_freq_max)
>+		return true;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++)
>+		if (test_bit(fs, &pin->prop.freq_supported))
>+			if (freq == dpll_pin_freq_value[fs])
>+				return true;
>+	return false;
>+}
>+
>+static int
>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>+		  struct netlink_ext_ack *extack)
>+{
>+	u32 freq = nla_get_u32(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) {
>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>+		if (ret)
>+			return -EFAULT;
>+		dpll_pin_notify(ref->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)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *parent;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	parent = dpll_pin_get_by_idx(dpll, parent_idx);
>+	if (!parent)
>+		return -EINVAL;
>+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+	if (!ref)
>+		return -EINVAL;
>+	if (!ref->ops || !ref->ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin, parent, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->state_on_dpll_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
>+		return -EINVAL;
>+	dpll_pin_notify(ref->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)
>+{
>+	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 = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->prio_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->prio_set(pin, 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) {
>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>+			return -EFAULT;
>+		dpll_pin_notify(ref->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;
>+	u32 parent_idx = PIN_IDX_INVALID;

You just need this PIN_IDX_INVALID define internally in this function,
change the flow to avoid a need for it.


>+	int rem, ret = -EINVAL;
>+	struct nlattr *a;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {

This is odd. Why you iterace over attrs? Why don't you just access them
directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?


>+		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_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) {

Again, change the flow to:
	if (attrs[DPLL_A_PIN_STATE]) {

and avoid need for this value set/check.


>+		if (parent_idx == PIN_IDX_INVALID) {
>+			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 nlattr *hdr, *nest;
>+	struct sk_buff *msg;
>+	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;
>+	nest = nla_nest_start(msg, DPLL_A_PIN);
>+	if (!nest)
>+		return -EMSGSIZE;
>+	ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack, true);
>+	if (ret) {
>+		nlmsg_free(msg);
>+		return ret;
>+	}
>+	nla_nest_end(msg, nest);
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_pin *pin;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_PIN_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each(&dpll_pin_xa, i, pin) {
>+		if (xa_empty(&pin->dpll_refs))
>+			continue;
>+		nest = nla_nest_start(skb, DPLL_A_PIN);
>+		if (!nest) {
>+			ret = -EMSGSIZE;
>+			break;
>+		}
>+		ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack, true);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}
>+		nla_nest_end(skb, nest);
>+	}
>+
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	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);
>+
>+			if (!dpll->ops || !dpll->ops->mode_set)
>+				return -EOPNOTSUPP;
>+			ret = dpll->ops->mode_set(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 nlattr *hdr, *nest;
>+	struct sk_buff *msg;
>+	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;
>+
>+	nest = nla_nest_start(msg, DPLL_A_DEVICE);
>+	ret = dpll_device_get_one(dpll, msg, info->extack);
>+	if (ret) {
>+		nlmsg_free(msg);
>+		return ret;
>+	}
>+	nla_nest_end(msg, nest);
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_device *dpll;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
>+		ret = dpll_msg_add_dev_handle(skb, dpll);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}
>+		nla_nest_end(skb, nest);
>+	}
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	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, *dpll_name = NULL;
>+	int ret = -ENODEV;
>+
>+	if (!info->attrs[DPLL_A_ID] &&
>+	    !(info->attrs[DPLL_A_BUS_NAME] && info->attrs[DPLL_A_DEV_NAME]))
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	if (info->attrs[DPLL_A_ID]) {
>+		u32 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;
>+	}
>+	if (info->attrs[DPLL_A_BUS_NAME] &&
>+	    info->attrs[DPLL_A_DEV_NAME]) {
>+		const char *bus_name = nla_data(info->attrs[DPLL_A_BUS_NAME]);
>+		const char *dev_name = nla_data(info->attrs[DPLL_A_DEV_NAME]);
>+
>+		dpll_name = dpll_device_get_by_name(bus_name, dev_name);
>+		if (!dpll_name) {
>+			ret = -ENODEV;
>+			goto unlock;
>+		}
>+
>+		if (dpll_id && dpll_name != dpll_id)
>+			goto unlock;
>+		info->user_ptr[0] = dpll_name;
>+	}
>+
>+	return 0;
>+unlock:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		    struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+
>+int dpll_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_device_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_device *dpll;
>+	struct dpll_pin *pin;
>+
>+	if (ret)
>+		return ret;
>+	dpll = info->user_ptr[0];
>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>+		ret = -EINVAL;
>+		goto unlock_dev;
>+	}
>+	mutex_lock(&dpll_pin_xa_lock);
>+	pin = dpll_pin_get_by_idx(dpll,
>+				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>+	if (!pin) {
>+		ret = -ENODEV;
>+		goto unlock_pin;
>+	}
>+	info->user_ptr[1] = pin;
>+
>+	return 0;
>+
>+unlock_pin:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+unlock_dev:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	dpll_post_doit(ops, skb, info);
>+}
>+
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_pin_xa_lock);

ABBA deadlock here, see dpll_pin_register() for example where the lock
taking order is opposite.


>+
>+	return dpll_pre_dumpit(cb);
>+}
>+
>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	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->dev_driver_id))
>+		return -EMSGSIZE;

I don't really understand why you are trying figure something new and
interesting with the change notifications. This object mix and random
attrs fillup is something very wrong and makes userspace completely
fuzzy about what it is getting. And yet it is so simple:
You have 2 objects, dpll and pin, please just have:
dpll_notify()
dpll_pin_notify()
and share the attrs fillup code with pin_get() and dpll_get() callbacks.
No need for any smartness. Have this dumb and simple.

Think about it more as about "object-state-snapshot" than "atomic-change"


>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_SOURCE_PIN_IDX:
>+		ret = dpll_msg_add_source_pin_idx(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:
>+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+			if (!ref)
>+				return -EFAULT;
>+			if (!ref->ops || !ref->ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>+							 NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->dev_driver_id))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+			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);

I don't really get it. Why exactly you keep having this *EVENT* cmds?
Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
I have to be missing something.


>+	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);
>+}
>+
>+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..072efa10f0e6
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,30 @@
>+/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr);
>+
>+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..db98b6d4bb73
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,284 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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;
>+
>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>+
>+struct dpll_device_ops {
>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
>+			struct netlink_ext_ack *extack);
>+	int (*mode_set)(const struct dpll_device *dpll,
>+			const enum dpll_mode mode,
>+			struct netlink_ext_ack *extack);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+			       const enum dpll_mode mode,
>+			       struct netlink_ext_ack *extack);
>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+				  u32 *pin_idx,
>+				  struct netlink_ext_ack *extack);
>+	int (*lock_status_get)(const struct dpll_device *dpll,
>+			       enum dpll_lock_status *status,
>+			       struct netlink_ext_ack *extack);
>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
>+			struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_ops {
>+	int (*frequency_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const u32 frequency,
>+			     struct netlink_ext_ack *extack);
>+	int (*frequency_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     u32 *frequency, struct netlink_ext_ack *extack);
>+	int (*direction_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const enum dpll_pin_direction direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*direction_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     enum dpll_pin_direction *direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*state_on_pin_get)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 enum dpll_pin_state *state,
>+				 struct netlink_ext_ack *extack);
>+	int (*state_on_pin_set)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 const enum dpll_pin_state state,
>+				 struct netlink_ext_ack *extack);
>+	int (*prio_get)(const struct dpll_pin *pin,
>+			const struct dpll_device *dpll,
>+			u32 *prio, struct netlink_ext_ack *extack);
>+	int (*prio_set)(const struct dpll_pin *pin,
>+			const struct dpll_device *dpll,
>+			const u32 prio, struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_properties {
>+	const char *description;
>+	enum dpll_pin_type type;
>+	unsigned long freq_supported;
>+	u32 any_freq_min;
>+	u32 any_freq_max;
>+	unsigned long capabilities;
>+};
>+
>+enum dpll_pin_freq_supp {
>+	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
>+	DPLL_PIN_FREQ_SUPP_1_HZ,
>+	DPLL_PIN_FREQ_SUPP_10_MHZ,
>+
>+	__DPLL_PIN_FREQ_SUPP_MAX,
>+	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
>+};
>+
>+/**
>+ * dpll_device_get - find or create dpll_device object
>+ * @clock_id: a system unique number for a device
>+ * @dev_driver_idx: 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,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner);
>+
>+/**
>+ * dpll_device_unregister - deregister registered dpll
>+ * @dpll: pointer to dpll
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_priv - get dpll private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private data pointer passed to dpll subsystem when allocating
>+ * device with ``dpll_device_alloc(..)``
>+ */
>+void *dpll_priv(const struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_on_pin_priv - get pin on pin pair private data
>+ * @parent: pointer to a parent pin
>+ * @pin: pointer to a dpll_pin
>+ *
>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>+ * was registered with parent pin.
>+ */
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_on_dpll_priv - get pin on dpll pair private data
>+ * @dpll: pointer to dpll
>+ * @pin: pointer to a dpll_pin
>+ *
>+ * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
>+ * was registered with a dpll.
>+ */
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_get - get reference or create new pin object
>+ * @clock_id: a system unique number of a device
>+ * @dev_driver_idx: 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_idx 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,

Name inconsistency: dev_driver_id/idx in comment


>+	      const struct dpll_pin_properties *pin_prop);

In .c you call this "prop", be consistent.


>+
>+/**
>+ * 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,
>+		      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
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ * Return:
>+ * * 0 - pin was successfully deregistered from this dpll device,
>+ * * -ENXIO - given pin was not registered with this dpll device,
>+ * * -EINVAL - pin pointer is not valid.
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
>+
>+/**
>+ * 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,
>+			     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);
>+
>+/**
>+ * 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);
>+
>+
>+#endif
>-- 
>2.34.1
>

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>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.
>
>Changes:
>dpll: adapt changes after introduction of dpll yaml spec
>dpll: redesign after review comments, fix minor issues
>dpll: add get pin command
>dpll: _get/_put approach for creating and realesing pin or dpll objects
>dpll: lock access to dplls with global lock
>dpll: lock access to pins with global lock
>
>dpll: replace cookie with clock id
>dpll: add clock class

Looks like some leftover in patch description.


>
>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: follow one naming schema in dpll subsys
>
>dpll: fix dpll device naming scheme
>
>Fix dpll device naming scheme by use of 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.
>
>dpll: remove description length parameter
>
>dpll: fix 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.
>
>dpll: move function comments to dpll_core.c, fix exports
>dpll: remove single-use helper functions
>dpll: merge device register with alloc
>dpll: lock and unlock mutex on dpll device release
>dpll: move dpll_type to uapi header
>dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>dpll: rename dpll_pin_state to dpll_pin_mode
>dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>dpll: remove DPLL_CHANGE_PIN_TYPE enum value

I'm confused, some messed-up squash?


>
>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                 |    9 +
> drivers/Kconfig             |    2 +
> drivers/Makefile            |    1 +
> drivers/dpll/Kconfig        |    7 +
> drivers/dpll/Makefile       |   10 +
> drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    |   99 ++++
> drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |   30 +
> include/linux/dpll.h        |  284 ++++++++++
> 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
>+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,10 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL)          += dpll_sys.o

What's "sys" and why is it here?


>+dpll_sys-y                  += dpll_core.o
>+dpll_sys-y                  += dpll_netlink.o
>+dpll_sys-y                  += dpll_nl.o
>+
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..3fc151e16751
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,835 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.
>+ *
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates

IIUC, there's a lot of Intel work behind this, I think some credits
should go here as well.

Also, it is 2023, you live in the past :)


>+ */
>+
>+#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_device_xa_lock);
>+DEFINE_MUTEX(dpll_pin_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))
>+
>+static struct class dpll_class = {
>+	.name = "dpll",
>+};
>+
>+/**
>+ * 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)
>+{
>+	struct dpll_device *dpll = NULL;
>+
>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>+		dpll = xa_load(&dpll_device_xa, id);
		return xa_load

>+
	return NULL

>+	return dpll;

Anyway, I believe this fuction should go away, see below.


>+}
>+
>+/**
>+ * 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 index;
>+
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>+		    !strcmp(dev_name(&dpll->dev), device_name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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 or increase refcount on existing pin
>+ * reference on given xarray.
>+ *
>+ * Return:
>+ * * 0 on success
>+ * * -ENOMEM on failed allocation
>+ */
>+static int
>+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;

In previous function you use "index" name for the same variable. Be
consistent. I think "i" is actually better.


>+	u32 idx;
>+	int ret;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			refcount_inc(&ref->refcount);
>+			return 0;
>+		}
>+	}
>+
>+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->pin = pin;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		refcount_set(&ref->refcount, 1);
>+	else
>+		kfree(ref);
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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 references are dropped, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_pins, i);
>+				kfree(ref);
>+			}
>+			return 0;
>+		}
>+	}
>+
>+	return -EINVAL;
>+}
>+
>+/**
>+ * dpll_xa_ref_pin_find - find pin reference on xarray
>+ * @xa_pins: dpll_pin_ref xarray holding pins
>+ * @pin: pointer to a pin
>+ *
>+ * Search for pin reference struct of a given pin 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_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin)
>+			return ref;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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,
>+		     struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 idx;
>+	int ret;
>+
>+	xa_for_each(xa_dplls, i, ref) {
>+		if (ref->dpll == dpll) {
>+			refcount_inc(&ref->refcount);
>+			return 0;
>+		}
>+	}
>+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->dpll = dpll;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		refcount_set(&ref->refcount, 1);
>+	else
>+		kfree(ref);
>+
>+	return ret;
>+}
>+
>+/**
>+ * 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)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_dplls, i, ref) {
>+		if (ref->dpll == dpll) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_dplls, i);
>+				kfree(ref);
>+			}
>+			break;
>+		}
>+	}
>+}
>+
>+/**
>+ * 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;
>+}
>+
>+
>+/**
>+ * dpll_device_alloc - allocate the memory for dpll device
>+ * @clock_id: clock_id of creator
>+ * @dev_driver_id: 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
>+ */
>+struct dpll_device *
>+dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)

This should be static. Didn't you get warn?


>+{
>+	struct dpll_device *dpll;
>+	int ret;
>+
>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>+	if (!dpll)
>+		return ERR_PTR(-ENOMEM);
>+	refcount_set(&dpll->refcount, 1);
>+	dpll->dev.class = &dpll_class;
>+	dpll->dev_driver_id = dev_driver_id;
>+	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
>+ * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each(&dpll_device_xa, index, dpll) {
>+		if (dpll->clock_id == clock_id &&
>+		    dpll->dev_driver_id == dev_driver_id &&

Why you need "dev_driver_id"? clock_id is here for the purpose of
identification, isn't that enough for you.

Plus, the name is odd. "dev_driver" should certainly be avoided.


>+		    dpll->module == module) {
>+			ret = dpll;
>+			refcount_inc(&ret->refcount);
>+			break;
>+		}
>+	}
>+	if (!ret)
>+		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>+	mutex_unlock(&dpll_device_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;

Remove this check. The driver should not call this with NULL.


>+	mutex_lock(&dpll_device_xa_lock);
>+	if (refcount_dec_and_test(&dpll->refcount)) {
>+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));

ASSERT_DPLL_NOT_REGISTERED(dpll);


>+		xa_destroy(&dpll->pin_refs);
>+		xa_erase(&dpll_device_xa, dpll->id);
>+		kfree(dpll);
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_put);
>+
>+/**
>+ * 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,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)
>+{
>+	if (WARN_ON(!ops || !owner))
>+		return -EINVAL;
>+	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>+		return -EINVAL;
>+	mutex_lock(&dpll_device_xa_lock);
>+	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>+		mutex_unlock(&dpll_device_xa_lock);
>+		return -EEXIST;
>+	}
>+	dpll->dev.bus = owner->bus;
>+	dpll->parent = owner;
>+	dpll->type = type;
>+	dpll->ops = ops;
>+	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>+		     dpll->dev_driver_id);

This is really odd. As a result, the user would see something like:
pci/0000:01:00.0_1
pci/0000:01:00.0_2

I have to say it is confusing. In devlink, is bus/name and the user
could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
there. Also, "_" might have some meaning on some bus. Should not
concatename dev_name() with anything.

Thinking about this some more, the module/clock_id tuple should be
uniqueue and stable. It is used for dpll_device_get(), it could be used
as the user handle, can't it?
Example:
ice/c92d02a7129f4747
mlx5/90265d8bf6e6df56

If you really need the "dev_driver_id" (as I believe clock_id should be
enough), you can put it here as well:
ice/c92d02a7129f4747/1
ice/c92d02a7129f4747/2

This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
share instance of DPLL equally, there is no "one clock master".



>+	dpll->priv = priv;
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_device_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
>+ *
>+ * Deregister device, make it unavailable for userspace.
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll)
>+{
>+	mutex_lock(&dpll_device_xa_lock);
>+	ASSERT_DPLL_REGISTERED(dpll);
>+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_device_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
>+ * @dev_driver_id: id 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

Extra "*"'s


>+ */
>+struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,

Odd whitespace.

Also, func should be static.


>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	pin->dev_driver_id = device_drv_id;

Name inconsistency: driver/drv
you have it on multiple places


>+	pin->clock_id = clock_id;
>+	pin->module = module;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->description)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>+	if (!pin->prop.description) {
>+		ret = -ENOMEM;
>+		goto release;
>+	}
>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.type = prop->type;
>+	pin->prop.capabilities = prop->capabilities;
>+	pin->prop.freq_supported = prop->freq_supported;
>+	pin->prop.any_freq_min = prop->any_freq_min;
>+	pin->prop.any_freq_max = prop->any_freq_max;

Make sure that the driver maintains prop (static const) and just save
the pointer. Prop does not need to be something driver needs to change.


>+	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->idx, pin,
>+		       xa_limit_16b, GFP_KERNEL);
>+release:
>+	if (ret) {
>+		xa_destroy(&pin->dpll_refs);
>+		xa_destroy(&pin->parent_refs);
>+		kfree(pin->prop.description);
>+		kfree(pin->rclk_dev_name);
>+		kfree(pin);
>+		return ERR_PTR(ret);
>+	}
>+
>+	return pin;
>+}
>+
>+/**
>+ * dpll_pin_get - find existing or create new dpll pin
>+ * @clock_id: clock_id of creator
>+ * @dev_driver_id: id 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

This is one example, I'm pretty sure that there are others, when you
have text inconsistencies in func doc for the same function in .c and .h
Have it please only on one place. .c is the usual place.


>+ */
>+struct dpll_pin *
>+dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,

Again, why do you need this device_drv_id? Clock id should be enough.


>+	     const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pos, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_pin_xa_lock);
>+	xa_for_each(&dpll_pin_xa, index, pos) {
>+		if (pos->clock_id == clock_id &&
>+		    pos->dev_driver_id == device_drv_id &&
>+		    pos->module == module) {

Compare prop as well.

Can't the driver_id (pin index) be something const as well? I think it
should. And therefore it could be easily put inside.


>+			ret = pos;
>+			refcount_inc(&ret->refcount);
>+			break;
>+		}
>+	}
>+	if (!ret)
>+		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	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;
>+	mutex_lock(&dpll_pin_xa_lock);
>+	if (refcount_dec_and_test(&pin->refcount)) {
>+		xa_destroy(&pin->dpll_refs);
>+		xa_destroy(&pin->parent_refs);
>+		xa_erase(&dpll_pin_xa, pin->idx);
>+		kfree(pin->prop.description);
>+		kfree(pin->rclk_dev_name);
>+		kfree(pin);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_put);
>+
>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	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);
>+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,
>+		  struct dpll_pin_ops *ops, void *priv,
>+		  struct device *rclk_device)

Wait a second, what is this "struct device *"? Looks very odd.


>+{
>+	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;

If you need to store something here, store the pointer to the device
directly. But this rclk_device seems odd to me.
Dev_name is in case of PCI device for example 0000:01:00.0? That alone
is incomplete. What should it server for?



>+	int ret;
>+
>+	if (WARN_ON(!dpll))
>+		return -EINVAL;
>+	if (WARN_ON(!pin))
>+		return -EINVAL;

Remove these checks and other similar checks in the code. It should rely
on basic driver sanity.


>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+static void
>+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
>+}
>+
>+/**
>+ * dpll_pin_unregister - deregister dpll pin from dpll device
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * Note: It does not free the memory
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>+		return -ENOENT;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	__dpll_pin_unregister(dpll, pin);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+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,
>+			 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(!pin || !parent))
>+		return -EINVAL;
>+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>+		return -EPERM;

I don't think that EPERM is suitable for this. Just use EINVAL. The
driver is buggy in this case anyway.


>+	mutex_lock(&dpll_pin_xa_lock);
>+	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_device_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_device_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_device_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin);
>+			mutex_unlock(&dpll_device_xa_lock);
>+		}
>+	}
>+	refcount_dec(&pin->refcount);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+unlock:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	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
>+ *
>+ * Note: It does not free the memory
>+ */
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+	refcount_dec(&pin->refcount);
>+	xa_for_each(&pin->dpll_refs, i, ref) {
>+		__dpll_pin_unregister(ref->dpll, pin);
>+		dpll_pin_parent_notify(ref->dpll, pin, parent,
>+				       DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>+
>+/**
>+ * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>+ * @dpll: dpll device pointer
>+ * @idx: index of pin
>+ *
>+ * Find a reference to a pin registered with given dpll and return its pointer.
>+ *
>+ * Return:
>+ * * valid pointer if pin was found
>+ * * NULL if not found
>+ */
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>+{
>+	struct dpll_pin_ref *pos;
>+	unsigned long i;
>+
>+	xa_for_each(&dpll->pin_refs, i, pos) {
>+		if (pos && pos->pin && pos->pin->dev_driver_id == idx)

How exactly pos->pin could be NULL?

Also, you are degrading the xarray to a mere list here with lookup like
this. Why can't you use the pin index coming from driver and
insert/lookup based on this index?


>+			return pos->pin;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * 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)
>+{
>+	return dpll->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_priv);
>+
>+/**
>+ * 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,

IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
Just pass the priv directly to the op and avoid need for this helper,
no? Same goes to the rest of the priv helpers.


>+			    const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);

Why cast is needed here? You have this on multiple places.


>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
>+
>+	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	ret = class_register(&dpll_class);

Why exactly do you need this? I asked to remove this previously, IIRC
you said you would check if this needed. Why?


>+	if (ret)
>+		goto unregister_netlink;
>+
>+	return 0;
>+
>+unregister_netlink:
>+	dpll_netlink_finish();
>+error:
>+	mutex_destroy(&dpll_device_xa_lock);
>+	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,99 @@
>+/* 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/refcount.h>
>+#include "dpll_netlink.h"
>+
>+#define DPLL_REGISTERED		XA_MARK_1
>+
>+/**
>+ * struct dpll_device - structure for a DPLL device
>+ * @id:			unique id number for each device
>+ * @dev_driver_id:	id given by dev driver
>+ * @dev:		struct device for this dpll device
>+ * @parent:		parent device
>+ * @module:		module of creator
>+ * @ops:		operations this &dpll_device supports
>+ * @lock:		mutex to serialize operations
>+ * @type:		type of a dpll
>+ * @priv:		pointer to private information of owner
>+ * @pins:		list of pointers to pins registered with this dpll
>+ * @clock_id:		unique identifier (clock_id) of a dpll
>+ * @mode_supported_mask: mask of supported modes
>+ * @refcount:		refcount
>+ **/
>+struct dpll_device {
>+	u32 id;
>+	u32 dev_driver_id;
>+	struct device dev;
>+	struct device *parent;
>+	struct module *module;
>+	struct dpll_device_ops *ops;
>+	enum dpll_type type;
>+	void *priv;
>+	struct xarray pin_refs;
>+	u64 clock_id;
>+	unsigned long mode_supported_mask;
>+	refcount_t refcount;
>+};
>+
>+/**
>+ * 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 idx;
>+	u32 dev_driver_id;
>+	u64 clock_id;
>+	struct module *module;

Have the ordering of common fields, like clock_id and module consistent
with struct dpll_device


>+	struct xarray dpll_refs;
>+	struct xarray parent_refs;
>+	struct dpll_pin_properties prop;
>+	char *rclk_dev_name;
>+	refcount_t refcount;
>+};
>+
>+/**
>+ * struct dpll_pin_ref - structure for referencing either dpll or pins
>+ * @dpll:		pointer to a dpll
>+ * @pin:		pointer to a pin
>+ * @ops:		ops for a dpll pin
>+ * @priv:		pointer to private information of owner
>+ **/
>+struct dpll_pin_ref {
>+	union {
>+		struct dpll_device *dpll;
>+		struct dpll_pin *pin;
>+	};
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+	refcount_t refcount;
>+};
>+
>+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);
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
>+struct dpll_pin_ref *
>+dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
>+struct dpll_pin_ref *
>+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
>+extern struct xarray dpll_device_xa;
>+extern struct xarray dpll_pin_xa;
>+extern struct mutex dpll_device_xa_lock;
>+extern struct mutex dpll_pin_xa_lock;
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..46aefeb1ac93
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,1065 @@
>+// 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>
>+
>+static u32 dpll_pin_freq_value[] = {
>+	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
>+	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
>+};
>+
>+static int
>+dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
>+{
>+	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))

Why exactly do we need this dua--handle scheme? Why do you need
unpredictable DPLL_A_ID to be exposed to userspace?
It's just confusing.



>+		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, const struct dpll_device *dpll,
>+		  struct netlink_ext_ack *extack)
>+{
>+	enum dpll_mode mode;
>+
>+	if (WARN_ON(!dpll->ops->mode_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->mode_get(dpll, &mode, extack))
>+		return -EFAULT;
>+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
>+			    struct netlink_ext_ack *extack)
>+{
>+	u32 source_pin_idx;
>+
>+	if (WARN_ON(!dpll->ops->source_pin_idx_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	enum dpll_lock_status status;
>+
>+	if (WARN_ON(!dpll->ops->lock_status_get))
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->lock_status_get(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)
>+{
>+	s32 temp;
>+
>+	if (!dpll->ops->temp_get)
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->temp_get(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)
>+{
>+	u32 prio;
>+
>+	if (!ref->ops->prio_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->prio_get(pin, ref->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)
>+{
>+	enum dpll_pin_state state;
>+
>+	if (!ref->ops->state_on_dpll_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_get(pin, ref->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 netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->direction_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))
>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref_parent;
>+	enum dpll_pin_state state;
>+	struct nlattr *nest;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(&pin->parent_refs, index, ref_parent) {
>+		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
>+			return -EFAULT;
>+		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
>+							&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,
>+				ref_parent->pin->dev_driver_id)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		nla_nest_end(msg, nest);
>+	}
>+
>+	return 0;
>+
>+nest_cancel:
>+	nla_nest_cancel(msg, nest);
>+	return ret;
>+}
>+
>+static int
>+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref = NULL;

Why this needs to be initialized?


>+	enum dpll_pin_state state;
>+	struct nlattr *nest;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(&pin->parent_refs, index, ref) {
>+		if (WARN_ON(!ref->ops->state_on_pin_get))
>+			return -EFAULT;
>+		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>+				ref->pin->dev_driver_id)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		nla_nest_end(msg, nest);
>+	}

How is this function different to dpll_msg_add_pin_parents()?
Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
hard to follow for me :/

Did you get lost here as well? If yes, this needs some serious think
through :)



>+
>+	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))

Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
code.

>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		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, bool dump_dpll)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
>+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (pin->rclk_dev_name)
>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;

Lots of code duplication with the previous functions, please unify.


>+
>+	return 0;
>+}
>+
>+static int
>+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
>+		     struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	enum dpll_mode mode;
>+	unsigned long i;
>+	int ret;
>+
>+	ret = dpll_msg_add_dev_handle(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_source_pin_idx(msg, dpll, extack);
>+	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;
>+	xa_for_each(&dpll->pin_refs, i, ref) {
>+		struct nlattr *nest = nla_nest_start(msg, DPLL_A_PIN);
>+
>+		if (!nest) {
>+			ret = -EMSGSIZE;
>+			break;
>+		}
>+		ret = dpll_cmd_pin_on_dpll_get(msg, ref->pin, dpll, extack);
>+		if (ret) {
>+			nla_nest_cancel(msg, nest);
>+			break;
>+		}
>+		nla_nest_end(msg, nest);
>+	}
>+
>+	return ret;
>+}
>+
>+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+
>+	if (freq >= pin->prop.any_freq_min && freq <= pin->prop.any_freq_max)
>+		return true;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++)
>+		if (test_bit(fs, &pin->prop.freq_supported))
>+			if (freq == dpll_pin_freq_value[fs])
>+				return true;
>+	return false;
>+}
>+
>+static int
>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>+		  struct netlink_ext_ack *extack)
>+{
>+	u32 freq = nla_get_u32(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) {
>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>+		if (ret)
>+			return -EFAULT;
>+		dpll_pin_notify(ref->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)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *parent;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	parent = dpll_pin_get_by_idx(dpll, parent_idx);
>+	if (!parent)
>+		return -EINVAL;
>+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+	if (!ref)
>+		return -EINVAL;
>+	if (!ref->ops || !ref->ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin, parent, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->state_on_dpll_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
>+		return -EINVAL;
>+	dpll_pin_notify(ref->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)
>+{
>+	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 = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->prio_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->prio_set(pin, 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) {
>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>+			return -EFAULT;
>+		dpll_pin_notify(ref->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;
>+	u32 parent_idx = PIN_IDX_INVALID;

You just need this PIN_IDX_INVALID define internally in this function,
change the flow to avoid a need for it.


>+	int rem, ret = -EINVAL;
>+	struct nlattr *a;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {

This is odd. Why you iterace over attrs? Why don't you just access them
directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?


>+		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_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) {

Again, change the flow to:
	if (attrs[DPLL_A_PIN_STATE]) {

and avoid need for this value set/check.


>+		if (parent_idx == PIN_IDX_INVALID) {
>+			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 nlattr *hdr, *nest;
>+	struct sk_buff *msg;
>+	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;
>+	nest = nla_nest_start(msg, DPLL_A_PIN);
>+	if (!nest)
>+		return -EMSGSIZE;
>+	ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack, true);
>+	if (ret) {
>+		nlmsg_free(msg);
>+		return ret;
>+	}
>+	nla_nest_end(msg, nest);
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_pin *pin;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_PIN_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each(&dpll_pin_xa, i, pin) {
>+		if (xa_empty(&pin->dpll_refs))
>+			continue;
>+		nest = nla_nest_start(skb, DPLL_A_PIN);
>+		if (!nest) {
>+			ret = -EMSGSIZE;
>+			break;
>+		}
>+		ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack, true);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}
>+		nla_nest_end(skb, nest);
>+	}
>+
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	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);
>+
>+			if (!dpll->ops || !dpll->ops->mode_set)
>+				return -EOPNOTSUPP;
>+			ret = dpll->ops->mode_set(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 nlattr *hdr, *nest;
>+	struct sk_buff *msg;
>+	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;
>+
>+	nest = nla_nest_start(msg, DPLL_A_DEVICE);
>+	ret = dpll_device_get_one(dpll, msg, info->extack);
>+	if (ret) {
>+		nlmsg_free(msg);
>+		return ret;
>+	}
>+	nla_nest_end(msg, nest);
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_device *dpll;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
>+		ret = dpll_msg_add_dev_handle(skb, dpll);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}
>+		nla_nest_end(skb, nest);
>+	}
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	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, *dpll_name = NULL;
>+	int ret = -ENODEV;
>+
>+	if (!info->attrs[DPLL_A_ID] &&
>+	    !(info->attrs[DPLL_A_BUS_NAME] && info->attrs[DPLL_A_DEV_NAME]))
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	if (info->attrs[DPLL_A_ID]) {
>+		u32 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;
>+	}
>+	if (info->attrs[DPLL_A_BUS_NAME] &&
>+	    info->attrs[DPLL_A_DEV_NAME]) {
>+		const char *bus_name = nla_data(info->attrs[DPLL_A_BUS_NAME]);
>+		const char *dev_name = nla_data(info->attrs[DPLL_A_DEV_NAME]);
>+
>+		dpll_name = dpll_device_get_by_name(bus_name, dev_name);
>+		if (!dpll_name) {
>+			ret = -ENODEV;
>+			goto unlock;
>+		}
>+
>+		if (dpll_id && dpll_name != dpll_id)
>+			goto unlock;
>+		info->user_ptr[0] = dpll_name;
>+	}
>+
>+	return 0;
>+unlock:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+		    struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+
>+int dpll_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+
>+int dpll_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_device_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_device *dpll;
>+	struct dpll_pin *pin;
>+
>+	if (ret)
>+		return ret;
>+	dpll = info->user_ptr[0];
>+	if (!info->attrs[DPLL_A_PIN_IDX]) {
>+		ret = -EINVAL;
>+		goto unlock_dev;
>+	}
>+	mutex_lock(&dpll_pin_xa_lock);
>+	pin = dpll_pin_get_by_idx(dpll,
>+				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>+	if (!pin) {
>+		ret = -ENODEV;
>+		goto unlock_pin;
>+	}
>+	info->user_ptr[1] = pin;
>+
>+	return 0;
>+
>+unlock_pin:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+unlock_dev:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	return ret;
>+}
>+
>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			struct genl_info *info)
>+{
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	dpll_post_doit(ops, skb, info);
>+}
>+
>+int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_lock(&dpll_pin_xa_lock);

ABBA deadlock here, see dpll_pin_register() for example where the lock
taking order is opposite.


>+
>+	return dpll_pre_dumpit(cb);
>+}
>+
>+int dpll_pin_post_dumpit(struct netlink_callback *cb)
>+{
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	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->dev_driver_id))
>+		return -EMSGSIZE;

I don't really understand why you are trying figure something new and
interesting with the change notifications. This object mix and random
attrs fillup is something very wrong and makes userspace completely
fuzzy about what it is getting. And yet it is so simple:
You have 2 objects, dpll and pin, please just have:
dpll_notify()
dpll_pin_notify()
and share the attrs fillup code with pin_get() and dpll_get() callbacks.
No need for any smartness. Have this dumb and simple.

Think about it more as about "object-state-snapshot" than "atomic-change"


>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_SOURCE_PIN_IDX:
>+		ret = dpll_msg_add_source_pin_idx(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:
>+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+			if (!ref)
>+				return -EFAULT;
>+			if (!ref->ops || !ref->ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>+							 NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->dev_driver_id))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+			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);

I don't really get it. Why exactly you keep having this *EVENT* cmds?
Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
I have to be missing something.


>+	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);
>+}
>+
>+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..072efa10f0e6
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,30 @@
>+/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dplla attr);
>+
>+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..db98b6d4bb73
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,284 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. 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;
>+
>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>+
>+struct dpll_device_ops {
>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
>+			struct netlink_ext_ack *extack);
>+	int (*mode_set)(const struct dpll_device *dpll,
>+			const enum dpll_mode mode,
>+			struct netlink_ext_ack *extack);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+			       const enum dpll_mode mode,
>+			       struct netlink_ext_ack *extack);
>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+				  u32 *pin_idx,
>+				  struct netlink_ext_ack *extack);
>+	int (*lock_status_get)(const struct dpll_device *dpll,
>+			       enum dpll_lock_status *status,
>+			       struct netlink_ext_ack *extack);
>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
>+			struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_ops {
>+	int (*frequency_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const u32 frequency,
>+			     struct netlink_ext_ack *extack);
>+	int (*frequency_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     u32 *frequency, struct netlink_ext_ack *extack);
>+	int (*direction_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const enum dpll_pin_direction direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*direction_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     enum dpll_pin_direction *direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*state_on_pin_get)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 enum dpll_pin_state *state,
>+				 struct netlink_ext_ack *extack);
>+	int (*state_on_pin_set)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 const enum dpll_pin_state state,
>+				 struct netlink_ext_ack *extack);
>+	int (*prio_get)(const struct dpll_pin *pin,
>+			const struct dpll_device *dpll,
>+			u32 *prio, struct netlink_ext_ack *extack);
>+	int (*prio_set)(const struct dpll_pin *pin,
>+			const struct dpll_device *dpll,
>+			const u32 prio, struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_properties {
>+	const char *description;
>+	enum dpll_pin_type type;
>+	unsigned long freq_supported;
>+	u32 any_freq_min;
>+	u32 any_freq_max;
>+	unsigned long capabilities;
>+};
>+
>+enum dpll_pin_freq_supp {
>+	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
>+	DPLL_PIN_FREQ_SUPP_1_HZ,
>+	DPLL_PIN_FREQ_SUPP_10_MHZ,
>+
>+	__DPLL_PIN_FREQ_SUPP_MAX,
>+	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
>+};
>+
>+/**
>+ * dpll_device_get - find or create dpll_device object
>+ * @clock_id: a system unique number for a device
>+ * @dev_driver_idx: 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,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner);
>+
>+/**
>+ * dpll_device_unregister - deregister registered dpll
>+ * @dpll: pointer to dpll
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_priv - get dpll private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private data pointer passed to dpll subsystem when allocating
>+ * device with ``dpll_device_alloc(..)``
>+ */
>+void *dpll_priv(const struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_on_pin_priv - get pin on pin pair private data
>+ * @parent: pointer to a parent pin
>+ * @pin: pointer to a dpll_pin
>+ *
>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>+ * was registered with parent pin.
>+ */
>+void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_on_dpll_priv - get pin on dpll pair private data
>+ * @dpll: pointer to dpll
>+ * @pin: pointer to a dpll_pin
>+ *
>+ * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
>+ * was registered with a dpll.
>+ */
>+void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_get - get reference or create new pin object
>+ * @clock_id: a system unique number of a device
>+ * @dev_driver_idx: 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_idx 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,

Name inconsistency: dev_driver_id/idx in comment


>+	      const struct dpll_pin_properties *pin_prop);

In .c you call this "prop", be consistent.


>+
>+/**
>+ * 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,
>+		      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
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ * Return:
>+ * * 0 - pin was successfully deregistered from this dpll device,
>+ * * -ENXIO - given pin was not registered with this dpll device,
>+ * * -EINVAL - pin pointer is not valid.
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
>+
>+/**
>+ * 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,
>+			     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);
>+
>+/**
>+ * 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);
>+
>+
>+#endif
>-- 
>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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-13 15:33     ` Vadim Fedorenko
@ 2023-03-13 16:22       ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-13 16:22 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>On 13/03/2023 12:20, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> > Implement common API for clock/DPLL configuration and status reporting.
>> > The API utilises netlink interface as transport for commands and event
>> > notifications. This API aim to extend current pin configuration and
>> > make it flexible and easy to cover special configurations.
>> 
>> Could you please put here some command line examples to work with this?
>
>We don't have open-source tools ready right now for specific hardware, but
>with YAML spec published you can use in-kernel tool to manipulate the values,
>i.e.:
>
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>device-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>device-get --json '{"id": 0}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>pin-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-get --json '{"id": 0, "pin-idx":1}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

Yep, that is exactly what I was asking for. Thanks.
Please try to extend it a bit and add to the cover letter. Gives people
better understanding of how this works.


>
>> 
>> > 
>> > 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
>> > 
>> > Vadim Fedorenko (3):
>> >   dpll: Add DPLL framework base functions
>> >   dpll: documentation on DPLL subsystem interface
>> >   ptp_ocp: implement DPLL ops
>> > 
>> > Documentation/netlink/specs/dpll.yaml         |  514 +++++
>> > Documentation/networking/dpll.rst             |  347 ++++
>> > Documentation/networking/index.rst            |    1 +
>> > MAINTAINERS                                   |    9 +
>> > drivers/Kconfig                               |    2 +
>> > drivers/Makefile                              |    1 +
>> > drivers/dpll/Kconfig                          |    7 +
>> > drivers/dpll/Makefile                         |   10 +
>> > drivers/dpll/dpll_core.c                      |  835 ++++++++
>> > drivers/dpll/dpll_core.h                      |   99 +
>> > drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>> > drivers/dpll/dpll_netlink.h                   |   30 +
>> > 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     | 1845 +++++++++++++++++
>> > drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>> > 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   |  411 ++++
>> > drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>> > drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>> > drivers/ptp/Kconfig                           |    1 +
>> > drivers/ptp/ptp_ocp.c                         |  206 +-
>> > include/linux/dpll.h                          |  284 +++
>> > include/uapi/linux/dpll.h                     |  196 ++
>> > 31 files changed, 7135 insertions(+), 16 deletions(-)
>> > create mode 100644 Documentation/netlink/specs/dpll.yaml
>> > create mode 100644 Documentation/networking/dpll.rst
>> > 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 include/linux/dpll.h
>> > create mode 100644 include/uapi/linux/dpll.h
>> > 
>> > -- 
>> > 2.34.1
>> > 
>

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-13 16:22       ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-13 16:22 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>On 13/03/2023 12:20, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> > Implement common API for clock/DPLL configuration and status reporting.
>> > The API utilises netlink interface as transport for commands and event
>> > notifications. This API aim to extend current pin configuration and
>> > make it flexible and easy to cover special configurations.
>> 
>> Could you please put here some command line examples to work with this?
>
>We don't have open-source tools ready right now for specific hardware, but
>with YAML spec published you can use in-kernel tool to manipulate the values,
>i.e.:
>
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>device-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>device-get --json '{"id": 0}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>pin-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-get --json '{"id": 0, "pin-idx":1}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

Yep, that is exactly what I was asking for. Thanks.
Please try to extend it a bit and add to the cover letter. Gives people
better understanding of how this works.


>
>> 
>> > 
>> > 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
>> > 
>> > Vadim Fedorenko (3):
>> >   dpll: Add DPLL framework base functions
>> >   dpll: documentation on DPLL subsystem interface
>> >   ptp_ocp: implement DPLL ops
>> > 
>> > Documentation/netlink/specs/dpll.yaml         |  514 +++++
>> > Documentation/networking/dpll.rst             |  347 ++++
>> > Documentation/networking/index.rst            |    1 +
>> > MAINTAINERS                                   |    9 +
>> > drivers/Kconfig                               |    2 +
>> > drivers/Makefile                              |    1 +
>> > drivers/dpll/Kconfig                          |    7 +
>> > drivers/dpll/Makefile                         |   10 +
>> > drivers/dpll/dpll_core.c                      |  835 ++++++++
>> > drivers/dpll/dpll_core.h                      |   99 +
>> > drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>> > drivers/dpll/dpll_netlink.h                   |   30 +
>> > 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     | 1845 +++++++++++++++++
>> > drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>> > 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   |  411 ++++
>> > drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>> > drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>> > drivers/ptp/Kconfig                           |    1 +
>> > drivers/ptp/ptp_ocp.c                         |  206 +-
>> > include/linux/dpll.h                          |  284 +++
>> > include/uapi/linux/dpll.h                     |  196 ++
>> > 31 files changed, 7135 insertions(+), 16 deletions(-)
>> > create mode 100644 Documentation/netlink/specs/dpll.yaml
>> > create mode 100644 Documentation/networking/dpll.rst
>> > 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 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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-13 16:22       ` Jiri Pirko
@ 2023-03-13 16:31         ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-13 16:31 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On 13/03/2023 16:22, Jiri Pirko wrote:
> Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>> On 13/03/2023 12:20, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>>>> Implement common API for clock/DPLL configuration and status reporting.
>>>> The API utilises netlink interface as transport for commands and event
>>>> notifications. This API aim to extend current pin configuration and
>>>> make it flexible and easy to cover special configurations.
>>>
>>> Could you please put here some command line examples to work with this?
>>
>> We don't have open-source tools ready right now for specific hardware, but
>> with YAML spec published you can use in-kernel tool to manipulate the values,
>> i.e.:
>>
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>> device-get
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> device-get --json '{"id": 0}'
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>> pin-get
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> pin-get --json '{"id": 0, "pin-idx":1}'
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'
> 
> Yep, that is exactly what I was asking for. Thanks.
> Please try to extend it a bit and add to the cover letter. Gives people
> better understanding of how this works.

Sure, will extend the cover letter in the next version, thanks!

> 
>>
>>>
>>>>
>>>> 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
>>>>
>>>> Vadim Fedorenko (3):
>>>>    dpll: Add DPLL framework base functions
>>>>    dpll: documentation on DPLL subsystem interface
>>>>    ptp_ocp: implement DPLL ops
>>>>
>>>> Documentation/netlink/specs/dpll.yaml         |  514 +++++
>>>> Documentation/networking/dpll.rst             |  347 ++++
>>>> Documentation/networking/index.rst            |    1 +
>>>> MAINTAINERS                                   |    9 +
>>>> drivers/Kconfig                               |    2 +
>>>> drivers/Makefile                              |    1 +
>>>> drivers/dpll/Kconfig                          |    7 +
>>>> drivers/dpll/Makefile                         |   10 +
>>>> drivers/dpll/dpll_core.c                      |  835 ++++++++
>>>> drivers/dpll/dpll_core.h                      |   99 +
>>>> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>>>> drivers/dpll/dpll_netlink.h                   |   30 +
>>>> 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     | 1845 +++++++++++++++++
>>>> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>>>> 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   |  411 ++++
>>>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>>>> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>>>> drivers/ptp/Kconfig                           |    1 +
>>>> drivers/ptp/ptp_ocp.c                         |  206 +-
>>>> include/linux/dpll.h                          |  284 +++
>>>> include/uapi/linux/dpll.h                     |  196 ++
>>>> 31 files changed, 7135 insertions(+), 16 deletions(-)
>>>> create mode 100644 Documentation/netlink/specs/dpll.yaml
>>>> create mode 100644 Documentation/networking/dpll.rst
>>>> 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 include/linux/dpll.h
>>>> create mode 100644 include/uapi/linux/dpll.h
>>>>
>>>> -- 
>>>> 2.34.1
>>>>
>>


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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-13 16:31         ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-13 16:31 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On 13/03/2023 16:22, Jiri Pirko wrote:
> Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>> On 13/03/2023 12:20, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>>>> Implement common API for clock/DPLL configuration and status reporting.
>>>> The API utilises netlink interface as transport for commands and event
>>>> notifications. This API aim to extend current pin configuration and
>>>> make it flexible and easy to cover special configurations.
>>>
>>> Could you please put here some command line examples to work with this?
>>
>> We don't have open-source tools ready right now for specific hardware, but
>> with YAML spec published you can use in-kernel tool to manipulate the values,
>> i.e.:
>>
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>> device-get
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> device-get --json '{"id": 0}'
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>> pin-get
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> pin-get --json '{"id": 0, "pin-idx":1}'
>> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>> pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'
> 
> Yep, that is exactly what I was asking for. Thanks.
> Please try to extend it a bit and add to the cover letter. Gives people
> better understanding of how this works.

Sure, will extend the cover letter in the next version, thanks!

> 
>>
>>>
>>>>
>>>> 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
>>>>
>>>> Vadim Fedorenko (3):
>>>>    dpll: Add DPLL framework base functions
>>>>    dpll: documentation on DPLL subsystem interface
>>>>    ptp_ocp: implement DPLL ops
>>>>
>>>> Documentation/netlink/specs/dpll.yaml         |  514 +++++
>>>> Documentation/networking/dpll.rst             |  347 ++++
>>>> Documentation/networking/index.rst            |    1 +
>>>> MAINTAINERS                                   |    9 +
>>>> drivers/Kconfig                               |    2 +
>>>> drivers/Makefile                              |    1 +
>>>> drivers/dpll/Kconfig                          |    7 +
>>>> drivers/dpll/Makefile                         |   10 +
>>>> drivers/dpll/dpll_core.c                      |  835 ++++++++
>>>> drivers/dpll/dpll_core.h                      |   99 +
>>>> drivers/dpll/dpll_netlink.c                   | 1065 ++++++++++
>>>> drivers/dpll/dpll_netlink.h                   |   30 +
>>>> 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     | 1845 +++++++++++++++++
>>>> drivers/net/ethernet/intel/ice/ice_dpll.h     |   96 +
>>>> 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   |  411 ++++
>>>> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 +++
>>>> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
>>>> drivers/ptp/Kconfig                           |    1 +
>>>> drivers/ptp/ptp_ocp.c                         |  206 +-
>>>> include/linux/dpll.h                          |  284 +++
>>>> include/uapi/linux/dpll.h                     |  196 ++
>>>> 31 files changed, 7135 insertions(+), 16 deletions(-)
>>>> create mode 100644 Documentation/netlink/specs/dpll.yaml
>>>> create mode 100644 Documentation/networking/dpll.rst
>>>> 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 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] 202+ messages in thread

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

On 13.03.2023 16:21, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>> 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.
>>
>> Changes:
>> dpll: adapt changes after introduction of dpll yaml spec
>> dpll: redesign after review comments, fix minor issues
>> dpll: add get pin command
>> dpll: _get/_put approach for creating and realesing pin or dpll objects
>> dpll: lock access to dplls with global lock
>> dpll: lock access to pins with global lock
>>
>> dpll: replace cookie with clock id
>> dpll: add clock class
> 
> Looks like some leftover in patch description.

Yeah, it's a changelog from the previous iteration.

>>
>> 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: follow one naming schema in dpll subsys
>>
>> dpll: fix dpll device naming scheme
>>
>> Fix dpll device naming scheme by use of 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.
>>
>> dpll: remove description length parameter
>>
>> dpll: fix 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.
>>
>> dpll: move function comments to dpll_core.c, fix exports
>> dpll: remove single-use helper functions
>> dpll: merge device register with alloc
>> dpll: lock and unlock mutex on dpll device release
>> dpll: move dpll_type to uapi header
>> dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>> dpll: rename dpll_pin_state to dpll_pin_mode
>> dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>> dpll: remove DPLL_CHANGE_PIN_TYPE enum value
> 
> I'm confused, some messed-up squash?

Yep, from previous iteration. I'll remove it next time.

>> 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                 |    9 +
>> drivers/Kconfig             |    2 +
>> drivers/Makefile            |    1 +
>> drivers/dpll/Kconfig        |    7 +
>> drivers/dpll/Makefile       |   10 +
>> drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    |   99 ++++
>> drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |   30 +
>> include/linux/dpll.h        |  284 ++++++++++
>> 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
>> +M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
>> --- /dev/null
>> +++ b/drivers/dpll/Makefile
>> @@ -0,0 +1,10 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Makefile for DPLL drivers.
>> +#
>> +
>> +obj-$(CONFIG_DPLL)          += dpll_sys.o
> 
> What's "sys" and why is it here?

It's an object file for the subsystem. Could be useful if we will have drivers
for DPLL-only devices.

>> +dpll_sys-y                  += dpll_core.o
>> +dpll_sys-y                  += dpll_netlink.o
>> +dpll_sys-y                  += dpll_nl.o
>> +
>> diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>> new file mode 100644
>> index 000000000000..3fc151e16751
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_core.c
>> @@ -0,0 +1,835 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  dpll_core.c - Generic DPLL Management class support.
>> + *
>> + *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
> 
> IIUC, there's a lot of Intel work behind this, I think some credits
> should go here as well.
> 
> Also, it is 2023, you live in the past :)

Yeah, that's true. Have to improve it. As well as in other files.

>> + */
>> +
>> +#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_device_xa_lock);
>> +DEFINE_MUTEX(dpll_pin_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))
>> +
>> +static struct class dpll_class = {
>> +	.name = "dpll",
>> +};
>> +
>> +/**
>> + * 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)
>> +{
>> +	struct dpll_device *dpll = NULL;
>> +
>> +	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>> +		dpll = xa_load(&dpll_device_xa, id);
> 		return xa_load
> 
>> +
> 	return NULL
> 
>> +	return dpll;
> 
> Anyway, I believe this fuction should go away, see below.

Don't think it matters, but happy to remove the function.

>> +}
>> +
>> +/**
>> + * 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 index;
>> +
>> +	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>> +		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>> +		    !strcmp(dev_name(&dpll->dev), device_name)) {
>> +			ret = dpll;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * 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 or increase refcount on existing pin
>> + * reference on given xarray.
>> + *
>> + * Return:
>> + * * 0 on success
>> + * * -ENOMEM on failed allocation
>> + */
>> +static int
>> +dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>> +		    struct dpll_pin_ops *ops, void *priv)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	unsigned long i;
> 
> In previous function you use "index" name for the same variable. Be
> consistent. I think "i" is actually better.
> 

Yep, agree.

> 
>> +	u32 idx;
>> +	int ret;
>> +
>> +	xa_for_each(xa_pins, i, ref) {
>> +		if (ref->pin == pin) {
>> +			refcount_inc(&ref->refcount);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>> +	if (!ref)
>> +		return -ENOMEM;
>> +	ref->pin = pin;
>> +	ref->ops = ops;
>> +	ref->priv = priv;
>> +	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
>> +	if (!ret)
>> +		refcount_set(&ref->refcount, 1);
>> +	else
>> +		kfree(ref);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * 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 references are dropped, delete the reference and free its memory.
>> + *
>> + * Return:
>> + * * 0 on success
>> + * * -EINVAL if reference to a pin was not found
>> + */

[...]

>> +/**
>> + * dpll_device_alloc - allocate the memory for dpll device
>> + * @clock_id: clock_id of creator
>> + * @dev_driver_id: 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
>> + */
>> +struct dpll_device *
>> +dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)
> 
> This should be static. Didn't you get warn?

I did get an email from kernel test robot. I'll make it static. As well as 
dpll_pin_alloc()

>> +{
>> +	struct dpll_device *dpll;
>> +	int ret;
>> +
>> +	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>> +	if (!dpll)
>> +		return ERR_PTR(-ENOMEM);
>> +	refcount_set(&dpll->refcount, 1);
>> +	dpll->dev.class = &dpll_class;
>> +	dpll->dev_driver_id = dev_driver_id;
>> +	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
>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>> +{
>> +	struct dpll_device *dpll, *ret = NULL;
>> +	unsigned long index;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>> +		if (dpll->clock_id == clock_id &&
>> +		    dpll->dev_driver_id == dev_driver_id &&
> 
> Why you need "dev_driver_id"? clock_id is here for the purpose of
> identification, isn't that enough for you.

dev_driver_id is needed to provide several DPLLs from one device. In ice driver
implementation there are 2 different DPLLs - to recover from PPS input and to 
recover from Sync-E. I believe there is only one clock, that's why clock id is 
the same for both of them. But Arkadiusz can tell more about it.
> 
> Plus, the name is odd. "dev_driver" should certainly be avoided.

Simply id doesn't tell anything either. dpll_dev_id?

>> +		    dpll->module == module) {
>> +			ret = dpll;
>> +			refcount_inc(&ret->refcount);
>> +			break;
>> +		}
>> +	}
>> +	if (!ret)
>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>> +	mutex_unlock(&dpll_device_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;
> 
> Remove this check. The driver should not call this with NULL.

Well, netdev_put() has this kind of check. As well as spi_dev_put() or
i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.

>> +	mutex_lock(&dpll_device_xa_lock);
>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
> 
> ASSERT_DPLL_NOT_REGISTERED(dpll);

Good point!

>> +		xa_destroy(&dpll->pin_refs);
>> +		xa_erase(&dpll_device_xa, dpll->id);
>> +		kfree(dpll);
>> +	}
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>> +
>> +/**
>> + * 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,
>> +			 struct dpll_device_ops *ops, void *priv,
>> +			 struct device *owner)
>> +{
>> +	if (WARN_ON(!ops || !owner))
>> +		return -EINVAL;
>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>> +		return -EINVAL;
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>> +		mutex_unlock(&dpll_device_xa_lock);
>> +		return -EEXIST;
>> +	}
>> +	dpll->dev.bus = owner->bus;
>> +	dpll->parent = owner;
>> +	dpll->type = type;
>> +	dpll->ops = ops;
>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>> +		     dpll->dev_driver_id);
> 
> This is really odd. As a result, the user would see something like:
> pci/0000:01:00.0_1
> pci/0000:01:00.0_2
> 
> I have to say it is confusing. In devlink, is bus/name and the user
> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
> there. Also, "_" might have some meaning on some bus. Should not
> concatename dev_name() with anything.
> 
> Thinking about this some more, the module/clock_id tuple should be
> uniqueue and stable. It is used for dpll_device_get(), it could be used
> as the user handle, can't it?
> Example:
> ice/c92d02a7129f4747
> mlx5/90265d8bf6e6df56
> 
> If you really need the "dev_driver_id" (as I believe clock_id should be
> enough), you can put it here as well:
> ice/c92d02a7129f4747/1
> ice/c92d02a7129f4747/2
>

Looks good, will change it

> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
> share instance of DPLL equally, there is no "one clock master". >
>> +	dpll->priv = priv;
>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>> +	mutex_unlock(&dpll_device_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
>> + *
>> + * Deregister device, make it unavailable for userspace.
>> + * Note: It does not free the memory
>> + */
>> +void dpll_device_unregister(struct dpll_device *dpll)
>> +{
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	ASSERT_DPLL_REGISTERED(dpll);
>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>> +	mutex_unlock(&dpll_device_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
>> + * @dev_driver_id: id 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
> 
> Extra "*"'s

Ok, I can re-format the comments across the files.

>> + */
>> +struct dpll_pin *
>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
> 
> Odd whitespace.
> 
> Also, func should be static.
> 

Fixed.

> 
>> +	       const struct dpll_pin_properties *prop)
>> +{
>> +	struct dpll_pin *pin;
>> +	int ret;
>> +
>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>> +	if (!pin)
>> +		return ERR_PTR(-ENOMEM);
>> +	pin->dev_driver_id = device_drv_id;
> 
> Name inconsistency: driver/drv
> you have it on multiple places
> 

Changed it every where, thanks for spotting.

> 
>> +	pin->clock_id = clock_id;
>> +	pin->module = module;
>> +	refcount_set(&pin->refcount, 1);
>> +	if (WARN_ON(!prop->description)) {
>> +		ret = -EINVAL;
>> +		goto release;
>> +	}
>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>> +	if (!pin->prop.description) {
>> +		ret = -ENOMEM;
>> +		goto release;
>> +	}
>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>> +		ret = -EINVAL;
>> +		goto release;
>> +	}
>> +	pin->prop.type = prop->type;
>> +	pin->prop.capabilities = prop->capabilities;
>> +	pin->prop.freq_supported = prop->freq_supported;
>> +	pin->prop.any_freq_min = prop->any_freq_min;
>> +	pin->prop.any_freq_max = prop->any_freq_max;
> 
> Make sure that the driver maintains prop (static const) and just save
> the pointer. Prop does not need to be something driver needs to change.
> 

What's the difference? For ptp_ocp, we have the same configuration for all
ext pins and the allocator only changes the name of the pin. Properties of
the DPLL pins are stored within the pin object, not the driver, in this case.
Not sure if the pointer way is much better...

> 
>> +	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->idx, pin,
>> +		       xa_limit_16b, GFP_KERNEL);
>> +release:
>> +	if (ret) {
>> +		xa_destroy(&pin->dpll_refs);
>> +		xa_destroy(&pin->parent_refs);
>> +		kfree(pin->prop.description);
>> +		kfree(pin->rclk_dev_name);
>> +		kfree(pin);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	return pin;
>> +}
>> +
>> +/**
>> + * dpll_pin_get - find existing or create new dpll pin
>> + * @clock_id: clock_id of creator
>> + * @dev_driver_id: id 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
> 
> This is one example, I'm pretty sure that there are others, when you
> have text inconsistencies in func doc for the same function in .c and .h
> Have it please only on one place. .c is the usual place.
> 

Yep, will clear .h files.

> 
>> + */
>> +struct dpll_pin *
>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
> 
> Again, why do you need this device_drv_id? Clock id should be enough.
> 
I explained the reason earlier, but the naming is fixed.

> 
>> +	     const struct dpll_pin_properties *prop)
>> +{
>> +	struct dpll_pin *pos, *ret = NULL;
>> +	unsigned long index;
>> +
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>> +		if (pos->clock_id == clock_id &&
>> +		    pos->dev_driver_id == device_drv_id &&
>> +		    pos->module == module) {
> 
> Compare prop as well.
> 
> Can't the driver_id (pin index) be something const as well? I think it
> should. And therefore it could be easily put inside.
> 

I think clock_id + dev_driver_id + module should identify the pin exactly. And 
now I think that *prop is not needed here at all. Arkadiusz, any thoughts?
> 
>> +			ret = pos;
>> +			refcount_inc(&ret->refcount);
>> +			break;
>> +		}
>> +	}
>> +	if (!ret)
>> +		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	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;
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	if (refcount_dec_and_test(&pin->refcount)) {
>> +		xa_destroy(&pin->dpll_refs);
>> +		xa_destroy(&pin->parent_refs);
>> +		xa_erase(&dpll_pin_xa, pin->idx);
>> +		kfree(pin->prop.description);
>> +		kfree(pin->rclk_dev_name);
>> +		kfree(pin);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_put);
>> +
>> +static int
>> +__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>> +		    struct dpll_pin_ops *ops, void *priv,
>> +		    const char *rclk_device_name)
>> +{
>> +	int ret;
>> +
>> +	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);
>> +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,
>> +		  struct dpll_pin_ops *ops, void *priv,
>> +		  struct device *rclk_device)
> 
> Wait a second, what is this "struct device *"? Looks very odd.
> 
> 
>> +{
>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
> 
> If you need to store something here, store the pointer to the device
> directly. But this rclk_device seems odd to me.
> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
> is incomplete. What should it server for?
> 

Well, these questions go to Arkadiusz...

> 
> 
>> +	int ret;
>> +
>> +	if (WARN_ON(!dpll))
>> +		return -EINVAL;
>> +	if (WARN_ON(!pin))
>> +		return -EINVAL;
> 
> Remove these checks and other similar checks in the code. It should rely
> on basic driver sanity.
> 

Ok...

> 
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_register);
>> +
>> +static void
>> +__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>> +{
>> +	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>> +	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
>> +}
>> +
>> +/**
>> + * dpll_pin_unregister - deregister dpll pin from dpll device
>> + * @dpll: registered dpll pointer
>> + * @pin: pointer to a pin
>> + *
>> + * Note: It does not free the memory
>> + */
>> +int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>> +{
>> +	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>> +		return -ENOENT;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	__dpll_pin_unregister(dpll, pin);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +
>> +	return 0;
>> +}
>> +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,
>> +			 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(!pin || !parent))
>> +		return -EINVAL;
>> +	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>> +		return -EPERM;
> 
> I don't think that EPERM is suitable for this. Just use EINVAL. The
> driver is buggy in this case anyway.
> 
   Ok.

> 
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	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_device_xa_lock);
>> +		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>> +					  rclk_device ?
>> +					  dev_name(rclk_device) : NULL);
>> +		mutex_unlock(&dpll_device_xa_lock);
>> +		if (ret) {
>> +			stop = i;
>> +			goto dpll_unregister;
>> +		}
>> +		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	return ret;
>> +
>> +dpll_unregister:
>> +	xa_for_each(&parent->dpll_refs, i, ref) {
>> +		if (i < stop) {
>> +			mutex_lock(&dpll_device_xa_lock);
>> +			__dpll_pin_unregister(ref->dpll, pin);
>> +			mutex_unlock(&dpll_device_xa_lock);
>> +		}
>> +	}
>> +	refcount_dec(&pin->refcount);
>> +	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>> +unlock:
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);

Now I realised that this function can bring us to ABBA deadlock with mutexes..


>> +
>> +/**
>> + * dpll_pin_on_pin_unregister - deregister dpll pin from a parent pin
>> + * @parent: pointer to a parent pin
>> + * @pin: pointer to a pin
>> + *
>> + * Note: It does not free the memory
>> + */
>> +void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	unsigned long i;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>> +	refcount_dec(&pin->refcount);
>> +	xa_for_each(&pin->dpll_refs, i, ref) {
>> +		__dpll_pin_unregister(ref->dpll, pin);
>> +		dpll_pin_parent_notify(ref->dpll, pin, parent,
>> +				       DPLL_A_PIN_IDX);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>> +
>> +/**
>> + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>> + * @dpll: dpll device pointer
>> + * @idx: index of pin
>> + *
>> + * Find a reference to a pin registered with given dpll and return its pointer.
>> + *
>> + * Return:
>> + * * valid pointer if pin was found
>> + * * NULL if not found
>> + */
>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>> +{
>> +	struct dpll_pin_ref *pos;
>> +	unsigned long i;
>> +
>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
> 
> How exactly pos->pin could be NULL?
> 
> Also, you are degrading the xarray to a mere list here with lookup like
> this. Why can't you use the pin index coming from driver and
> insert/lookup based on this index?
> 
Good point. We just have to be sure, that drivers provide 0-based indexes for 
their pins. I'll re-think it.


> 
>> +			return pos->pin;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * 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)
>> +{
>> +	return dpll->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_priv);
>> +
>> +/**
>> + * 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,
> 
> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
> Just pass the priv directly to the op and avoid need for this helper,
> no? Same goes to the rest of the priv helpers.
> 
> 
>> +			    const struct dpll_pin *pin)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +
>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
> 
> Why cast is needed here? You have this on multiple places.
> 
> 
>> +	if (!ref)
>> +		return NULL;
>> +
>> +	return ref->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
>> +
>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
>> +	if (!ref)
>> +		return NULL;
>> +
>> +	return ref->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
>> +
>> +static int __init dpll_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = dpll_netlink_init();
>> +	if (ret)
>> +		goto error;
>> +
>> +	ret = class_register(&dpll_class);
> 
> Why exactly do you need this? I asked to remove this previously, IIRC
> you said you would check if this needed. Why?
> 
Ah, sorry. Removed it now.


> 
>> +	if (ret)
>> +		goto unregister_netlink;
>> +
>> +	return 0;
>> +
>> +unregister_netlink:
>> +	dpll_netlink_finish();
>> +error:
>> +	mutex_destroy(&dpll_device_xa_lock);
>> +	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_core.h
>> @@ -0,0 +1,99 @@
>> +/* 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/refcount.h>
>> +#include "dpll_netlink.h"
>> +
>> +#define DPLL_REGISTERED		XA_MARK_1
>> +
>> +/**
>> + * struct dpll_device - structure for a DPLL device
>> + * @id:			unique id number for each device
>> + * @dev_driver_id:	id given by dev driver
>> + * @dev:		struct device for this dpll device
>> + * @parent:		parent device
>> + * @module:		module of creator
>> + * @ops:		operations this &dpll_device supports
>> + * @lock:		mutex to serialize operations
>> + * @type:		type of a dpll
>> + * @priv:		pointer to private information of owner
>> + * @pins:		list of pointers to pins registered with this dpll
>> + * @clock_id:		unique identifier (clock_id) of a dpll
>> + * @mode_supported_mask: mask of supported modes
>> + * @refcount:		refcount
>> + **/
>> +struct dpll_device {
>> +	u32 id;
>> +	u32 dev_driver_id;
>> +	struct device dev;
>> +	struct device *parent;
>> +	struct module *module;
>> +	struct dpll_device_ops *ops;
>> +	enum dpll_type type;
>> +	void *priv;
>> +	struct xarray pin_refs;
>> +	u64 clock_id;
>> +	unsigned long mode_supported_mask;
>> +	refcount_t refcount;
>> +};
>> +
>> +/**
>> + * 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 idx;
>> +	u32 dev_driver_id;
>> +	u64 clock_id;
>> +	struct module *module;
> 
> Have the ordering of common fields, like clock_id and module consistent
> with struct dpll_device
> 

Re-arranged it a bit.

> 
>> +	struct xarray dpll_refs;
>> +	struct xarray parent_refs;
>> +	struct dpll_pin_properties prop;
>> +	char *rclk_dev_name;
>> +	refcount_t refcount;
>> +};
>> +
>> +/**
>> + * struct dpll_pin_ref - structure for referencing either dpll or pins
>> + * @dpll:		pointer to a dpll
>> + * @pin:		pointer to a pin
>> + * @ops:		ops for a dpll pin
>> + * @priv:		pointer to private information of owner
>> + **/
>> +struct dpll_pin_ref {
>> +	union {
>> +		struct dpll_device *dpll;
>> +		struct dpll_pin *pin;
>> +	};
>> +	struct dpll_pin_ops *ops;
>> +	void *priv;
>> +	refcount_t refcount;
>> +};
>> +
>> +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);
>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
>> +struct dpll_pin_ref *
>> +dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
>> +struct dpll_pin_ref *
>> +dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
>> +extern struct xarray dpll_device_xa;
>> +extern struct xarray dpll_pin_xa;
>> +extern struct mutex dpll_device_xa_lock;
>> +extern struct mutex dpll_pin_xa_lock;
>> +#endif
>> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>> new file mode 100644
>> index 000000000000..46aefeb1ac93
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_netlink.c
>> @@ -0,0 +1,1065 @@
>> +// 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>
>> +
>> +static u32 dpll_pin_freq_value[] = {
>> +	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
>> +	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
>> +};
>> +
>> +static int
>> +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
>> +{
>> +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
> 
> Why exactly do we need this dua--handle scheme? Why do you need
> unpredictable DPLL_A_ID to be exposed to userspace?
> It's just confusing.
> 
To be able to work with DPLL per integer after iterator on the list deducts
which DPLL device is needed. It can reduce the amount of memory copies and
simplify comparisons. Not sure why it's confusing.

> 
>> +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>> +			 struct netlink_ext_ack *extack)
>> +{
>> +	struct dpll_pin_ref *ref = NULL;
> 
> Why this needs to be initialized?
> 
No need, fixed.


> 
>> +	enum dpll_pin_state state;
>> +	struct nlattr *nest;
>> +	unsigned long index;
>> +	int ret;
>> +
>> +	xa_for_each(&pin->parent_refs, index, ref) {
>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>> +			return -EFAULT;
>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>> +				ref->pin->dev_driver_id)) {
>> +			ret = -EMSGSIZE;
>> +			goto nest_cancel;
>> +		}
>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>> +			ret = -EMSGSIZE;
>> +			goto nest_cancel;
>> +		}
>> +		nla_nest_end(msg, nest);
>> +	}
> 
> How is this function different to dpll_msg_add_pin_parents()?
> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
> hard to follow for me :/
> 
> Did you get lost here as well? If yes, this needs some serious think
> through :)
> 

Let's re-think it again. Arkadiuzs, do you have clear explanation of the
relationship between these things?

> 
>> +
>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>> +			 struct dpll_device *dpll,
>> +			 struct netlink_ext_ack *extack)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	int ret;
>> +
>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
> 
> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
> code.
> 

I believe it's INDEX which is provided by the driver. I'll think about renaming,
but suggestions are welcome.

>> +		return -EMSGSIZE;
>> +	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>> +		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, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +	if (!ref)
>> +		return -EFAULT;
>> +	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>> +	if (ret)
>> +		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, bool dump_dpll)
>> +{
>> +	int ret;
>> +
>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>> +		return -EMSGSIZE;
>> +	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>> +		return -EMSGSIZE;
>> +	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>> +		return -EMSGSIZE;
>> +	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>> +	if (ret)
>> +		return ret;
>> +	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
>> +		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	if (pin->rclk_dev_name)
>> +		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>> +				   pin->rclk_dev_name))
>> +			return -EMSGSIZE;
> 
> Lots of code duplication with the previous functions, please unify.
> 

Agree, have done it.

> 
>> +
>> +	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;
>> +	u32 parent_idx = PIN_IDX_INVALID;
> 
> You just need this PIN_IDX_INVALID define internally in this function,
> change the flow to avoid a need for it.
> 

I'll re-think it, thanks.

> 
>> +	int rem, ret = -EINVAL;
>> +	struct nlattr *a;
>> +
>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>> +			  genlmsg_len(info->genlhdr), rem) {
> 
> This is odd. Why you iterace over attrs? Why don't you just access them
> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
> 

I had some unknown crashes when I was using such access. I might have lost some
checks, will try it again.

> 
>> +		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_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) {
> 
> Again, change the flow to:
> 	if (attrs[DPLL_A_PIN_STATE]) {
> 
> and avoid need for this value set/check.
> 

Yep, will try.

> 
>> +		if (parent_idx == PIN_IDX_INVALID) {
>> +			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_pre_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_lock(&dpll_device_xa_lock);
>> +
>> +	return 0;
>> +}
>> +
>> +int dpll_post_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_unlock(&dpll_device_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_device *dpll;
>> +	struct dpll_pin *pin;
>> +
>> +	if (ret)
>> +		return ret;
>> +	dpll = info->user_ptr[0];
>> +	if (!info->attrs[DPLL_A_PIN_IDX]) {
>> +		ret = -EINVAL;
>> +		goto unlock_dev;
>> +	}
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	pin = dpll_pin_get_by_idx(dpll,
>> +				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>> +	if (!pin) {
>> +		ret = -ENODEV;
>> +		goto unlock_pin;
>> +	}
>> +	info->user_ptr[1] = pin;
>> +
>> +	return 0;
>> +
>> +unlock_pin:
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +unlock_dev:
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +	return ret;
>> +}
>> +
>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>> +			struct genl_info *info)
>> +{
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	dpll_post_doit(ops, skb, info);
>> +}
>> +
>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_lock(&dpll_pin_xa_lock);
> 
> ABBA deadlock here, see dpll_pin_register() for example where the lock
> taking order is opposite.
> 

Now I see an ABBA deadlock here, as well as in function before. Not sure how to
solve it here. Any thoughts?

> 
>> +
>> +	return dpll_pre_dumpit(cb);
>> +}
>> +
>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	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->dev_driver_id))
>> +		return -EMSGSIZE;
> 
> I don't really understand why you are trying figure something new and
> interesting with the change notifications. This object mix and random
> attrs fillup is something very wrong and makes userspace completely
> fuzzy about what it is getting. And yet it is so simple:
> You have 2 objects, dpll and pin, please just have:
> dpll_notify()
> dpll_pin_notify()
> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
> No need for any smartness. Have this dumb and simple.
> 
> Think about it more as about "object-state-snapshot" than "atomic-change" 

But with full object-snapshot user space app will lose the information about
what exactly has changed. The reason to have this event is to provide the 
attributes which have changed. Otherwise, the app should have full snapshot and
compare all attributes to figure out changes and that's might not be great idea.

> 
>> +
>> +	switch (attr) {
>> +	case DPLL_A_MODE:
>> +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>> +		break;
>> +	case DPLL_A_SOURCE_PIN_IDX:
>> +		ret = dpll_msg_add_source_pin_idx(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:
>> +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>> +		break;
>> +	case DPLL_A_PIN_PRIO:
>> +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +		if (!ref)
>> +			return -EFAULT;
>> +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>> +		break;
>> +	case DPLL_A_PIN_STATE:
>> +		if (parent) {
>> +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>> +			if (!ref)
>> +				return -EFAULT;
>> +			if (!ref->ops || !ref->ops->state_on_pin_get)
>> +				return -EOPNOTSUPP;
>> +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>> +							 NULL);
>> +			if (ret)
>> +				return ret;
>> +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>> +					parent->dev_driver_id))
>> +				return -EMSGSIZE;
>> +		} else {
>> +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +			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);
> 
> I don't really get it. Why exactly you keep having this *EVENT* cmds?
> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
> I have to be missing something.

Changes might come from other places, but will affect the DPLL device and we
have to notify users in this case.
> 
> 
>> +	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);
>> +}
>> +
>> +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..072efa10f0e6
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_netlink.h
>> @@ -0,0 +1,30 @@
>> +/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>> +		    enum dplla attr);
>> +
>> +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..db98b6d4bb73
>> --- /dev/null
>> +++ b/include/linux/dpll.h
>> @@ -0,0 +1,284 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + *  Copyright (c) 2021 Meta Platforms, Inc. 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;
>> +
>> +#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>> +
>> +struct dpll_device_ops {
>> +	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
>> +			struct netlink_ext_ack *extack);
>> +	int (*mode_set)(const struct dpll_device *dpll,
>> +			const enum dpll_mode mode,
>> +			struct netlink_ext_ack *extack);
>> +	bool (*mode_supported)(const struct dpll_device *dpll,
>> +			       const enum dpll_mode mode,
>> +			       struct netlink_ext_ack *extack);
>> +	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>> +				  u32 *pin_idx,
>> +				  struct netlink_ext_ack *extack);
>> +	int (*lock_status_get)(const struct dpll_device *dpll,
>> +			       enum dpll_lock_status *status,
>> +			       struct netlink_ext_ack *extack);
>> +	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
>> +			struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_ops {
>> +	int (*frequency_set)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     const u32 frequency,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*frequency_get)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     u32 *frequency, struct netlink_ext_ack *extack);
>> +	int (*direction_set)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     const enum dpll_pin_direction direction,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*direction_get)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     enum dpll_pin_direction *direction,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*state_on_pin_get)(const struct dpll_pin *pin,
>> +				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,
>> +				 const struct dpll_device *dpll,
>> +				 enum dpll_pin_state *state,
>> +				 struct netlink_ext_ack *extack);
>> +	int (*state_on_pin_set)(const struct dpll_pin *pin,
>> +				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,
>> +				 const struct dpll_device *dpll,
>> +				 const enum dpll_pin_state state,
>> +				 struct netlink_ext_ack *extack);
>> +	int (*prio_get)(const struct dpll_pin *pin,
>> +			const struct dpll_device *dpll,
>> +			u32 *prio, struct netlink_ext_ack *extack);
>> +	int (*prio_set)(const struct dpll_pin *pin,
>> +			const struct dpll_device *dpll,
>> +			const u32 prio, struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_properties {
>> +	const char *description;
>> +	enum dpll_pin_type type;
>> +	unsigned long freq_supported;
>> +	u32 any_freq_min;
>> +	u32 any_freq_max;
>> +	unsigned long capabilities;
>> +};
>> +
>> +enum dpll_pin_freq_supp {
>> +	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
>> +	DPLL_PIN_FREQ_SUPP_1_HZ,
>> +	DPLL_PIN_FREQ_SUPP_10_MHZ,
>> +
>> +	__DPLL_PIN_FREQ_SUPP_MAX,
>> +	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
>> +};
>> +
>> +/**
>> + * dpll_device_get - find or create dpll_device object
>> + * @clock_id: a system unique number for a device
>> + * @dev_driver_idx: 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,
>> +			 struct dpll_device_ops *ops, void *priv,
>> +			 struct device *owner);
>> +
>> +/**
>> + * dpll_device_unregister - deregister registered dpll
>> + * @dpll: pointer to dpll
>> + *
>> + * Unregister the dpll from the subsystem, make it unavailable for netlink
>> + * API users.
>> + */
>> +void dpll_device_unregister(struct dpll_device *dpll);
>> +
>> +/**
>> + * dpll_priv - get dpll private data
>> + * @dpll: pointer to dpll
>> + *
>> + * Obtain private data pointer passed to dpll subsystem when allocating
>> + * device with ``dpll_device_alloc(..)``
>> + */
>> +void *dpll_priv(const struct dpll_device *dpll);
>> +
>> +/**
>> + * dpll_pin_on_pin_priv - get pin on pin pair private data
>> + * @parent: pointer to a parent pin
>> + * @pin: pointer to a dpll_pin
>> + *
>> + * Obtain private pin data pointer passed to dpll subsystem when pin
>> + * was registered with parent pin.
>> + */
>> +void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
>> +
>> +/**
>> + * dpll_pin_on_dpll_priv - get pin on dpll pair private data
>> + * @dpll: pointer to dpll
>> + * @pin: pointer to a dpll_pin
>> + *
>> + * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
>> + * was registered with a dpll.
>> + */
>> +void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>> +
>> +/**
>> + * dpll_pin_get - get reference or create new pin object
>> + * @clock_id: a system unique number of a device
>> + * @dev_driver_idx: 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_idx 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,
> 
> Name inconsistency: dev_driver_id/idx in comment
> 
> 
>> +	      const struct dpll_pin_properties *pin_prop);
> 
> In .c you call this "prop", be consistent.
> 

Fixed both.

> 
>> +
>> +/**
>> + * 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,
>> +		      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
>> + *
>> + * Deregister previously registered pin object from a dpll device.
>> + *
>> + * Return:
>> + * * 0 - pin was successfully deregistered from this dpll device,
>> + * * -ENXIO - given pin was not registered with this dpll device,
>> + * * -EINVAL - pin pointer is not valid.
>> + */
>> +int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
>> +
>> +/**
>> + * 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,
>> +			     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);
>> +
>> +/**
>> + * 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);
>> +
>> +
>> +#endif
>> -- 
>> 2.34.1
>>


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

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

On 13.03.2023 16:21, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>> 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.
>>
>> Changes:
>> dpll: adapt changes after introduction of dpll yaml spec
>> dpll: redesign after review comments, fix minor issues
>> dpll: add get pin command
>> dpll: _get/_put approach for creating and realesing pin or dpll objects
>> dpll: lock access to dplls with global lock
>> dpll: lock access to pins with global lock
>>
>> dpll: replace cookie with clock id
>> dpll: add clock class
> 
> Looks like some leftover in patch description.

Yeah, it's a changelog from the previous iteration.

>>
>> 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: follow one naming schema in dpll subsys
>>
>> dpll: fix dpll device naming scheme
>>
>> Fix dpll device naming scheme by use of 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.
>>
>> dpll: remove description length parameter
>>
>> dpll: fix 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.
>>
>> dpll: move function comments to dpll_core.c, fix exports
>> dpll: remove single-use helper functions
>> dpll: merge device register with alloc
>> dpll: lock and unlock mutex on dpll device release
>> dpll: move dpll_type to uapi header
>> dpll: rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>> dpll: rename dpll_pin_state to dpll_pin_mode
>> dpll: rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>> dpll: remove DPLL_CHANGE_PIN_TYPE enum value
> 
> I'm confused, some messed-up squash?

Yep, from previous iteration. I'll remove it next time.

>> 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                 |    9 +
>> drivers/Kconfig             |    2 +
>> drivers/Makefile            |    1 +
>> drivers/dpll/Kconfig        |    7 +
>> drivers/dpll/Makefile       |   10 +
>> drivers/dpll/dpll_core.c    |  835 +++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    |   99 ++++
>> drivers/dpll/dpll_netlink.c | 1065 +++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |   30 +
>> include/linux/dpll.h        |  284 ++++++++++
>> 10 files changed, 2342 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 edd3d562beee..0222b19af545 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6289,6 +6289,15 @@ 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 <vadim.fedorenko@linux.dev>
>> +M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.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..d3926f2a733d
>> --- /dev/null
>> +++ b/drivers/dpll/Makefile
>> @@ -0,0 +1,10 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Makefile for DPLL drivers.
>> +#
>> +
>> +obj-$(CONFIG_DPLL)          += dpll_sys.o
> 
> What's "sys" and why is it here?

It's an object file for the subsystem. Could be useful if we will have drivers
for DPLL-only devices.

>> +dpll_sys-y                  += dpll_core.o
>> +dpll_sys-y                  += dpll_netlink.o
>> +dpll_sys-y                  += dpll_nl.o
>> +
>> diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>> new file mode 100644
>> index 000000000000..3fc151e16751
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_core.c
>> @@ -0,0 +1,835 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  dpll_core.c - Generic DPLL Management class support.
>> + *
>> + *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
> 
> IIUC, there's a lot of Intel work behind this, I think some credits
> should go here as well.
> 
> Also, it is 2023, you live in the past :)

Yeah, that's true. Have to improve it. As well as in other files.

>> + */
>> +
>> +#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_device_xa_lock);
>> +DEFINE_MUTEX(dpll_pin_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))
>> +
>> +static struct class dpll_class = {
>> +	.name = "dpll",
>> +};
>> +
>> +/**
>> + * 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)
>> +{
>> +	struct dpll_device *dpll = NULL;
>> +
>> +	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>> +		dpll = xa_load(&dpll_device_xa, id);
> 		return xa_load
> 
>> +
> 	return NULL
> 
>> +	return dpll;
> 
> Anyway, I believe this fuction should go away, see below.

Don't think it matters, but happy to remove the function.

>> +}
>> +
>> +/**
>> + * 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 index;
>> +
>> +	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>> +		if (!strcmp(dev_bus_name(&dpll->dev), bus_name) &&
>> +		    !strcmp(dev_name(&dpll->dev), device_name)) {
>> +			ret = dpll;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * 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 or increase refcount on existing pin
>> + * reference on given xarray.
>> + *
>> + * Return:
>> + * * 0 on success
>> + * * -ENOMEM on failed allocation
>> + */
>> +static int
>> +dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>> +		    struct dpll_pin_ops *ops, void *priv)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	unsigned long i;
> 
> In previous function you use "index" name for the same variable. Be
> consistent. I think "i" is actually better.
> 

Yep, agree.

> 
>> +	u32 idx;
>> +	int ret;
>> +
>> +	xa_for_each(xa_pins, i, ref) {
>> +		if (ref->pin == pin) {
>> +			refcount_inc(&ref->refcount);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>> +	if (!ref)
>> +		return -ENOMEM;
>> +	ref->pin = pin;
>> +	ref->ops = ops;
>> +	ref->priv = priv;
>> +	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
>> +	if (!ret)
>> +		refcount_set(&ref->refcount, 1);
>> +	else
>> +		kfree(ref);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * 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 references are dropped, delete the reference and free its memory.
>> + *
>> + * Return:
>> + * * 0 on success
>> + * * -EINVAL if reference to a pin was not found
>> + */

[...]

>> +/**
>> + * dpll_device_alloc - allocate the memory for dpll device
>> + * @clock_id: clock_id of creator
>> + * @dev_driver_id: 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
>> + */
>> +struct dpll_device *
>> +dpll_device_alloc(const u64 clock_id, u32 dev_driver_id, struct module *module)
> 
> This should be static. Didn't you get warn?

I did get an email from kernel test robot. I'll make it static. As well as 
dpll_pin_alloc()

>> +{
>> +	struct dpll_device *dpll;
>> +	int ret;
>> +
>> +	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>> +	if (!dpll)
>> +		return ERR_PTR(-ENOMEM);
>> +	refcount_set(&dpll->refcount, 1);
>> +	dpll->dev.class = &dpll_class;
>> +	dpll->dev_driver_id = dev_driver_id;
>> +	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
>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>> +{
>> +	struct dpll_device *dpll, *ret = NULL;
>> +	unsigned long index;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>> +		if (dpll->clock_id == clock_id &&
>> +		    dpll->dev_driver_id == dev_driver_id &&
> 
> Why you need "dev_driver_id"? clock_id is here for the purpose of
> identification, isn't that enough for you.

dev_driver_id is needed to provide several DPLLs from one device. In ice driver
implementation there are 2 different DPLLs - to recover from PPS input and to 
recover from Sync-E. I believe there is only one clock, that's why clock id is 
the same for both of them. But Arkadiusz can tell more about it.
> 
> Plus, the name is odd. "dev_driver" should certainly be avoided.

Simply id doesn't tell anything either. dpll_dev_id?

>> +		    dpll->module == module) {
>> +			ret = dpll;
>> +			refcount_inc(&ret->refcount);
>> +			break;
>> +		}
>> +	}
>> +	if (!ret)
>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>> +	mutex_unlock(&dpll_device_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;
> 
> Remove this check. The driver should not call this with NULL.

Well, netdev_put() has this kind of check. As well as spi_dev_put() or
i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.

>> +	mutex_lock(&dpll_device_xa_lock);
>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
> 
> ASSERT_DPLL_NOT_REGISTERED(dpll);

Good point!

>> +		xa_destroy(&dpll->pin_refs);
>> +		xa_erase(&dpll_device_xa, dpll->id);
>> +		kfree(dpll);
>> +	}
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>> +
>> +/**
>> + * 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,
>> +			 struct dpll_device_ops *ops, void *priv,
>> +			 struct device *owner)
>> +{
>> +	if (WARN_ON(!ops || !owner))
>> +		return -EINVAL;
>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>> +		return -EINVAL;
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>> +		mutex_unlock(&dpll_device_xa_lock);
>> +		return -EEXIST;
>> +	}
>> +	dpll->dev.bus = owner->bus;
>> +	dpll->parent = owner;
>> +	dpll->type = type;
>> +	dpll->ops = ops;
>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>> +		     dpll->dev_driver_id);
> 
> This is really odd. As a result, the user would see something like:
> pci/0000:01:00.0_1
> pci/0000:01:00.0_2
> 
> I have to say it is confusing. In devlink, is bus/name and the user
> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
> there. Also, "_" might have some meaning on some bus. Should not
> concatename dev_name() with anything.
> 
> Thinking about this some more, the module/clock_id tuple should be
> uniqueue and stable. It is used for dpll_device_get(), it could be used
> as the user handle, can't it?
> Example:
> ice/c92d02a7129f4747
> mlx5/90265d8bf6e6df56
> 
> If you really need the "dev_driver_id" (as I believe clock_id should be
> enough), you can put it here as well:
> ice/c92d02a7129f4747/1
> ice/c92d02a7129f4747/2
>

Looks good, will change it

> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
> share instance of DPLL equally, there is no "one clock master". >
>> +	dpll->priv = priv;
>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>> +	mutex_unlock(&dpll_device_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
>> + *
>> + * Deregister device, make it unavailable for userspace.
>> + * Note: It does not free the memory
>> + */
>> +void dpll_device_unregister(struct dpll_device *dpll)
>> +{
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	ASSERT_DPLL_REGISTERED(dpll);
>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>> +	mutex_unlock(&dpll_device_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
>> + * @dev_driver_id: id 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
> 
> Extra "*"'s

Ok, I can re-format the comments across the files.

>> + */
>> +struct dpll_pin *
>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
> 
> Odd whitespace.
> 
> Also, func should be static.
> 

Fixed.

> 
>> +	       const struct dpll_pin_properties *prop)
>> +{
>> +	struct dpll_pin *pin;
>> +	int ret;
>> +
>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>> +	if (!pin)
>> +		return ERR_PTR(-ENOMEM);
>> +	pin->dev_driver_id = device_drv_id;
> 
> Name inconsistency: driver/drv
> you have it on multiple places
> 

Changed it every where, thanks for spotting.

> 
>> +	pin->clock_id = clock_id;
>> +	pin->module = module;
>> +	refcount_set(&pin->refcount, 1);
>> +	if (WARN_ON(!prop->description)) {
>> +		ret = -EINVAL;
>> +		goto release;
>> +	}
>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>> +	if (!pin->prop.description) {
>> +		ret = -ENOMEM;
>> +		goto release;
>> +	}
>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>> +		ret = -EINVAL;
>> +		goto release;
>> +	}
>> +	pin->prop.type = prop->type;
>> +	pin->prop.capabilities = prop->capabilities;
>> +	pin->prop.freq_supported = prop->freq_supported;
>> +	pin->prop.any_freq_min = prop->any_freq_min;
>> +	pin->prop.any_freq_max = prop->any_freq_max;
> 
> Make sure that the driver maintains prop (static const) and just save
> the pointer. Prop does not need to be something driver needs to change.
> 

What's the difference? For ptp_ocp, we have the same configuration for all
ext pins and the allocator only changes the name of the pin. Properties of
the DPLL pins are stored within the pin object, not the driver, in this case.
Not sure if the pointer way is much better...

> 
>> +	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->idx, pin,
>> +		       xa_limit_16b, GFP_KERNEL);
>> +release:
>> +	if (ret) {
>> +		xa_destroy(&pin->dpll_refs);
>> +		xa_destroy(&pin->parent_refs);
>> +		kfree(pin->prop.description);
>> +		kfree(pin->rclk_dev_name);
>> +		kfree(pin);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	return pin;
>> +}
>> +
>> +/**
>> + * dpll_pin_get - find existing or create new dpll pin
>> + * @clock_id: clock_id of creator
>> + * @dev_driver_id: id 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
> 
> This is one example, I'm pretty sure that there are others, when you
> have text inconsistencies in func doc for the same function in .c and .h
> Have it please only on one place. .c is the usual place.
> 

Yep, will clear .h files.

> 
>> + */
>> +struct dpll_pin *
>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
> 
> Again, why do you need this device_drv_id? Clock id should be enough.
> 
I explained the reason earlier, but the naming is fixed.

> 
>> +	     const struct dpll_pin_properties *prop)
>> +{
>> +	struct dpll_pin *pos, *ret = NULL;
>> +	unsigned long index;
>> +
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>> +		if (pos->clock_id == clock_id &&
>> +		    pos->dev_driver_id == device_drv_id &&
>> +		    pos->module == module) {
> 
> Compare prop as well.
> 
> Can't the driver_id (pin index) be something const as well? I think it
> should. And therefore it could be easily put inside.
> 

I think clock_id + dev_driver_id + module should identify the pin exactly. And 
now I think that *prop is not needed here at all. Arkadiusz, any thoughts?
> 
>> +			ret = pos;
>> +			refcount_inc(&ret->refcount);
>> +			break;
>> +		}
>> +	}
>> +	if (!ret)
>> +		ret = dpll_pin_alloc(clock_id, device_drv_id, module, prop);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	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;
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	if (refcount_dec_and_test(&pin->refcount)) {
>> +		xa_destroy(&pin->dpll_refs);
>> +		xa_destroy(&pin->parent_refs);
>> +		xa_erase(&dpll_pin_xa, pin->idx);
>> +		kfree(pin->prop.description);
>> +		kfree(pin->rclk_dev_name);
>> +		kfree(pin);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_put);
>> +
>> +static int
>> +__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>> +		    struct dpll_pin_ops *ops, void *priv,
>> +		    const char *rclk_device_name)
>> +{
>> +	int ret;
>> +
>> +	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);
>> +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,
>> +		  struct dpll_pin_ops *ops, void *priv,
>> +		  struct device *rclk_device)
> 
> Wait a second, what is this "struct device *"? Looks very odd.
> 
> 
>> +{
>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
> 
> If you need to store something here, store the pointer to the device
> directly. But this rclk_device seems odd to me.
> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
> is incomplete. What should it server for?
> 

Well, these questions go to Arkadiusz...

> 
> 
>> +	int ret;
>> +
>> +	if (WARN_ON(!dpll))
>> +		return -EINVAL;
>> +	if (WARN_ON(!pin))
>> +		return -EINVAL;
> 
> Remove these checks and other similar checks in the code. It should rely
> on basic driver sanity.
> 

Ok...

> 
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	ret = __dpll_pin_register(dpll, pin, ops, priv, rclk_name);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_register);
>> +
>> +static void
>> +__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>> +{
>> +	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>> +	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
>> +}
>> +
>> +/**
>> + * dpll_pin_unregister - deregister dpll pin from dpll device
>> + * @dpll: registered dpll pointer
>> + * @pin: pointer to a pin
>> + *
>> + * Note: It does not free the memory
>> + */
>> +int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
>> +{
>> +	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>> +		return -ENOENT;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	__dpll_pin_unregister(dpll, pin);
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +
>> +	return 0;
>> +}
>> +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,
>> +			 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(!pin || !parent))
>> +		return -EINVAL;
>> +	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>> +		return -EPERM;
> 
> I don't think that EPERM is suitable for this. Just use EINVAL. The
> driver is buggy in this case anyway.
> 
   Ok.

> 
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	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_device_xa_lock);
>> +		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,
>> +					  rclk_device ?
>> +					  dev_name(rclk_device) : NULL);
>> +		mutex_unlock(&dpll_device_xa_lock);
>> +		if (ret) {
>> +			stop = i;
>> +			goto dpll_unregister;
>> +		}
>> +		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	return ret;
>> +
>> +dpll_unregister:
>> +	xa_for_each(&parent->dpll_refs, i, ref) {
>> +		if (i < stop) {
>> +			mutex_lock(&dpll_device_xa_lock);
>> +			__dpll_pin_unregister(ref->dpll, pin);
>> +			mutex_unlock(&dpll_device_xa_lock);
>> +		}
>> +	}
>> +	refcount_dec(&pin->refcount);
>> +	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>> +unlock:
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);

Now I realised that this function can bring us to ABBA deadlock with mutexes..


>> +
>> +/**
>> + * dpll_pin_on_pin_unregister - deregister dpll pin from a parent pin
>> + * @parent: pointer to a parent pin
>> + * @pin: pointer to a pin
>> + *
>> + * Note: It does not free the memory
>> + */
>> +void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	unsigned long i;
>> +
>> +	mutex_lock(&dpll_device_xa_lock);
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>> +	refcount_dec(&pin->refcount);
>> +	xa_for_each(&pin->dpll_refs, i, ref) {
>> +		__dpll_pin_unregister(ref->dpll, pin);
>> +		dpll_pin_parent_notify(ref->dpll, pin, parent,
>> +				       DPLL_A_PIN_IDX);
>> +	}
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>> +
>> +/**
>> + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>> + * @dpll: dpll device pointer
>> + * @idx: index of pin
>> + *
>> + * Find a reference to a pin registered with given dpll and return its pointer.
>> + *
>> + * Return:
>> + * * valid pointer if pin was found
>> + * * NULL if not found
>> + */
>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>> +{
>> +	struct dpll_pin_ref *pos;
>> +	unsigned long i;
>> +
>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
> 
> How exactly pos->pin could be NULL?
> 
> Also, you are degrading the xarray to a mere list here with lookup like
> this. Why can't you use the pin index coming from driver and
> insert/lookup based on this index?
> 
Good point. We just have to be sure, that drivers provide 0-based indexes for 
their pins. I'll re-think it.


> 
>> +			return pos->pin;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * 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)
>> +{
>> +	return dpll->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_priv);
>> +
>> +/**
>> + * 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,
> 
> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
> Just pass the priv directly to the op and avoid need for this helper,
> no? Same goes to the rest of the priv helpers.
> 
> 
>> +			    const struct dpll_pin *pin)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +
>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
> 
> Why cast is needed here? You have this on multiple places.
> 
> 
>> +	if (!ref)
>> +		return NULL;
>> +
>> +	return ref->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
>> +
>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
>> +	if (!ref)
>> +		return NULL;
>> +
>> +	return ref->priv;
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
>> +
>> +static int __init dpll_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = dpll_netlink_init();
>> +	if (ret)
>> +		goto error;
>> +
>> +	ret = class_register(&dpll_class);
> 
> Why exactly do you need this? I asked to remove this previously, IIRC
> you said you would check if this needed. Why?
> 
Ah, sorry. Removed it now.


> 
>> +	if (ret)
>> +		goto unregister_netlink;
>> +
>> +	return 0;
>> +
>> +unregister_netlink:
>> +	dpll_netlink_finish();
>> +error:
>> +	mutex_destroy(&dpll_device_xa_lock);
>> +	mutex_destroy(&dpll_pin_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..876b6ac6f3a0
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_core.h
>> @@ -0,0 +1,99 @@
>> +/* 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/refcount.h>
>> +#include "dpll_netlink.h"
>> +
>> +#define DPLL_REGISTERED		XA_MARK_1
>> +
>> +/**
>> + * struct dpll_device - structure for a DPLL device
>> + * @id:			unique id number for each device
>> + * @dev_driver_id:	id given by dev driver
>> + * @dev:		struct device for this dpll device
>> + * @parent:		parent device
>> + * @module:		module of creator
>> + * @ops:		operations this &dpll_device supports
>> + * @lock:		mutex to serialize operations
>> + * @type:		type of a dpll
>> + * @priv:		pointer to private information of owner
>> + * @pins:		list of pointers to pins registered with this dpll
>> + * @clock_id:		unique identifier (clock_id) of a dpll
>> + * @mode_supported_mask: mask of supported modes
>> + * @refcount:		refcount
>> + **/
>> +struct dpll_device {
>> +	u32 id;
>> +	u32 dev_driver_id;
>> +	struct device dev;
>> +	struct device *parent;
>> +	struct module *module;
>> +	struct dpll_device_ops *ops;
>> +	enum dpll_type type;
>> +	void *priv;
>> +	struct xarray pin_refs;
>> +	u64 clock_id;
>> +	unsigned long mode_supported_mask;
>> +	refcount_t refcount;
>> +};
>> +
>> +/**
>> + * 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 idx;
>> +	u32 dev_driver_id;
>> +	u64 clock_id;
>> +	struct module *module;
> 
> Have the ordering of common fields, like clock_id and module consistent
> with struct dpll_device
> 

Re-arranged it a bit.

> 
>> +	struct xarray dpll_refs;
>> +	struct xarray parent_refs;
>> +	struct dpll_pin_properties prop;
>> +	char *rclk_dev_name;
>> +	refcount_t refcount;
>> +};
>> +
>> +/**
>> + * struct dpll_pin_ref - structure for referencing either dpll or pins
>> + * @dpll:		pointer to a dpll
>> + * @pin:		pointer to a pin
>> + * @ops:		ops for a dpll pin
>> + * @priv:		pointer to private information of owner
>> + **/
>> +struct dpll_pin_ref {
>> +	union {
>> +		struct dpll_device *dpll;
>> +		struct dpll_pin *pin;
>> +	};
>> +	struct dpll_pin_ops *ops;
>> +	void *priv;
>> +	refcount_t refcount;
>> +};
>> +
>> +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);
>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
>> +struct dpll_pin_ref *
>> +dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
>> +struct dpll_pin_ref *
>> +dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
>> +extern struct xarray dpll_device_xa;
>> +extern struct xarray dpll_pin_xa;
>> +extern struct mutex dpll_device_xa_lock;
>> +extern struct mutex dpll_pin_xa_lock;
>> +#endif
>> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>> new file mode 100644
>> index 000000000000..46aefeb1ac93
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_netlink.c
>> @@ -0,0 +1,1065 @@
>> +// 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>
>> +
>> +static u32 dpll_pin_freq_value[] = {
>> +	[DPLL_PIN_FREQ_SUPP_1_HZ] = DPLL_PIN_FREQ_1_HZ,
>> +	[DPLL_PIN_FREQ_SUPP_10_MHZ] = DPLL_PIN_FREQ_10_MHZ,
>> +};
>> +
>> +static int
>> +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
>> +{
>> +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
> 
> Why exactly do we need this dua--handle scheme? Why do you need
> unpredictable DPLL_A_ID to be exposed to userspace?
> It's just confusing.
> 
To be able to work with DPLL per integer after iterator on the list deducts
which DPLL device is needed. It can reduce the amount of memory copies and
simplify comparisons. Not sure why it's confusing.

> 
>> +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>> +			 struct netlink_ext_ack *extack)
>> +{
>> +	struct dpll_pin_ref *ref = NULL;
> 
> Why this needs to be initialized?
> 
No need, fixed.


> 
>> +	enum dpll_pin_state state;
>> +	struct nlattr *nest;
>> +	unsigned long index;
>> +	int ret;
>> +
>> +	xa_for_each(&pin->parent_refs, index, ref) {
>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>> +			return -EFAULT;
>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>> +				ref->pin->dev_driver_id)) {
>> +			ret = -EMSGSIZE;
>> +			goto nest_cancel;
>> +		}
>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>> +			ret = -EMSGSIZE;
>> +			goto nest_cancel;
>> +		}
>> +		nla_nest_end(msg, nest);
>> +	}
> 
> How is this function different to dpll_msg_add_pin_parents()?
> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
> hard to follow for me :/
> 
> Did you get lost here as well? If yes, this needs some serious think
> through :)
> 

Let's re-think it again. Arkadiuzs, do you have clear explanation of the
relationship between these things?

> 
>> +
>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>> +			 struct dpll_device *dpll,
>> +			 struct netlink_ext_ack *extack)
>> +{
>> +	struct dpll_pin_ref *ref;
>> +	int ret;
>> +
>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
> 
> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
> code.
> 

I believe it's INDEX which is provided by the driver. I'll think about renaming,
but suggestions are welcome.

>> +		return -EMSGSIZE;
>> +	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>> +		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, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +	if (!ref)
>> +		return -EFAULT;
>> +	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>> +	if (ret)
>> +		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, bool dump_dpll)
>> +{
>> +	int ret;
>> +
>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>> +		return -EMSGSIZE;
>> +	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>> +		return -EMSGSIZE;
>> +	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>> +		return -EMSGSIZE;
>> +	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>> +	if (ret && ret != -EOPNOTSUPP)
>> +		return ret;
>> +	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>> +	if (ret)
>> +		return ret;
>> +	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {
>> +		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	if (pin->rclk_dev_name)
>> +		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>> +				   pin->rclk_dev_name))
>> +			return -EMSGSIZE;
> 
> Lots of code duplication with the previous functions, please unify.
> 

Agree, have done it.

> 
>> +
>> +	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;
>> +	u32 parent_idx = PIN_IDX_INVALID;
> 
> You just need this PIN_IDX_INVALID define internally in this function,
> change the flow to avoid a need for it.
> 

I'll re-think it, thanks.

> 
>> +	int rem, ret = -EINVAL;
>> +	struct nlattr *a;
>> +
>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>> +			  genlmsg_len(info->genlhdr), rem) {
> 
> This is odd. Why you iterace over attrs? Why don't you just access them
> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
> 

I had some unknown crashes when I was using such access. I might have lost some
checks, will try it again.

> 
>> +		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_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) {
> 
> Again, change the flow to:
> 	if (attrs[DPLL_A_PIN_STATE]) {
> 
> and avoid need for this value set/check.
> 

Yep, will try.

> 
>> +		if (parent_idx == PIN_IDX_INVALID) {
>> +			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_pre_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_lock(&dpll_device_xa_lock);
>> +
>> +	return 0;
>> +}
>> +
>> +int dpll_post_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_unlock(&dpll_device_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_device *dpll;
>> +	struct dpll_pin *pin;
>> +
>> +	if (ret)
>> +		return ret;
>> +	dpll = info->user_ptr[0];
>> +	if (!info->attrs[DPLL_A_PIN_IDX]) {
>> +		ret = -EINVAL;
>> +		goto unlock_dev;
>> +	}
>> +	mutex_lock(&dpll_pin_xa_lock);
>> +	pin = dpll_pin_get_by_idx(dpll,
>> +				  nla_get_u32(info->attrs[DPLL_A_PIN_IDX]));
>> +	if (!pin) {
>> +		ret = -ENODEV;
>> +		goto unlock_pin;
>> +	}
>> +	info->user_ptr[1] = pin;
>> +
>> +	return 0;
>> +
>> +unlock_pin:
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +unlock_dev:
>> +	mutex_unlock(&dpll_device_xa_lock);
>> +	return ret;
>> +}
>> +
>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>> +			struct genl_info *info)
>> +{
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +	dpll_post_doit(ops, skb, info);
>> +}
>> +
>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_lock(&dpll_pin_xa_lock);
> 
> ABBA deadlock here, see dpll_pin_register() for example where the lock
> taking order is opposite.
> 

Now I see an ABBA deadlock here, as well as in function before. Not sure how to
solve it here. Any thoughts?

> 
>> +
>> +	return dpll_pre_dumpit(cb);
>> +}
>> +
>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>> +{
>> +	mutex_unlock(&dpll_pin_xa_lock);
>> +
>> +	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->dev_driver_id))
>> +		return -EMSGSIZE;
> 
> I don't really understand why you are trying figure something new and
> interesting with the change notifications. This object mix and random
> attrs fillup is something very wrong and makes userspace completely
> fuzzy about what it is getting. And yet it is so simple:
> You have 2 objects, dpll and pin, please just have:
> dpll_notify()
> dpll_pin_notify()
> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
> No need for any smartness. Have this dumb and simple.
> 
> Think about it more as about "object-state-snapshot" than "atomic-change" 

But with full object-snapshot user space app will lose the information about
what exactly has changed. The reason to have this event is to provide the 
attributes which have changed. Otherwise, the app should have full snapshot and
compare all attributes to figure out changes and that's might not be great idea.

> 
>> +
>> +	switch (attr) {
>> +	case DPLL_A_MODE:
>> +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>> +		break;
>> +	case DPLL_A_SOURCE_PIN_IDX:
>> +		ret = dpll_msg_add_source_pin_idx(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:
>> +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>> +		break;
>> +	case DPLL_A_PIN_PRIO:
>> +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +		if (!ref)
>> +			return -EFAULT;
>> +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>> +		break;
>> +	case DPLL_A_PIN_STATE:
>> +		if (parent) {
>> +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>> +			if (!ref)
>> +				return -EFAULT;
>> +			if (!ref->ops || !ref->ops->state_on_pin_get)
>> +				return -EOPNOTSUPP;
>> +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>> +							 NULL);
>> +			if (ret)
>> +				return ret;
>> +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>> +					parent->dev_driver_id))
>> +				return -EMSGSIZE;
>> +		} else {
>> +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> +			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);
> 
> I don't really get it. Why exactly you keep having this *EVENT* cmds?
> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
> I have to be missing something.

Changes might come from other places, but will affect the DPLL device and we
have to notify users in this case.
> 
> 
>> +	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);
>> +}
>> +
>> +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..072efa10f0e6
>> --- /dev/null
>> +++ b/drivers/dpll/dpll_netlink.h
>> @@ -0,0 +1,30 @@
>> +/* 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_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>> +		    enum dplla attr);
>> +
>> +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..db98b6d4bb73
>> --- /dev/null
>> +++ b/include/linux/dpll.h
>> @@ -0,0 +1,284 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + *  Copyright (c) 2021 Meta Platforms, Inc. 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;
>> +
>> +#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>> +
>> +struct dpll_device_ops {
>> +	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode,
>> +			struct netlink_ext_ack *extack);
>> +	int (*mode_set)(const struct dpll_device *dpll,
>> +			const enum dpll_mode mode,
>> +			struct netlink_ext_ack *extack);
>> +	bool (*mode_supported)(const struct dpll_device *dpll,
>> +			       const enum dpll_mode mode,
>> +			       struct netlink_ext_ack *extack);
>> +	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>> +				  u32 *pin_idx,
>> +				  struct netlink_ext_ack *extack);
>> +	int (*lock_status_get)(const struct dpll_device *dpll,
>> +			       enum dpll_lock_status *status,
>> +			       struct netlink_ext_ack *extack);
>> +	int (*temp_get)(const struct dpll_device *dpll, s32 *temp,
>> +			struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_ops {
>> +	int (*frequency_set)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     const u32 frequency,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*frequency_get)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     u32 *frequency, struct netlink_ext_ack *extack);
>> +	int (*direction_set)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     const enum dpll_pin_direction direction,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*direction_get)(const struct dpll_pin *pin,
>> +			     const struct dpll_device *dpll,
>> +			     enum dpll_pin_direction *direction,
>> +			     struct netlink_ext_ack *extack);
>> +	int (*state_on_pin_get)(const struct dpll_pin *pin,
>> +				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,
>> +				 const struct dpll_device *dpll,
>> +				 enum dpll_pin_state *state,
>> +				 struct netlink_ext_ack *extack);
>> +	int (*state_on_pin_set)(const struct dpll_pin *pin,
>> +				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,
>> +				 const struct dpll_device *dpll,
>> +				 const enum dpll_pin_state state,
>> +				 struct netlink_ext_ack *extack);
>> +	int (*prio_get)(const struct dpll_pin *pin,
>> +			const struct dpll_device *dpll,
>> +			u32 *prio, struct netlink_ext_ack *extack);
>> +	int (*prio_set)(const struct dpll_pin *pin,
>> +			const struct dpll_device *dpll,
>> +			const u32 prio, struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_properties {
>> +	const char *description;
>> +	enum dpll_pin_type type;
>> +	unsigned long freq_supported;
>> +	u32 any_freq_min;
>> +	u32 any_freq_max;
>> +	unsigned long capabilities;
>> +};
>> +
>> +enum dpll_pin_freq_supp {
>> +	DPLL_PIN_FREQ_SUPP_UNSPEC = 0,
>> +	DPLL_PIN_FREQ_SUPP_1_HZ,
>> +	DPLL_PIN_FREQ_SUPP_10_MHZ,
>> +
>> +	__DPLL_PIN_FREQ_SUPP_MAX,
>> +	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
>> +};
>> +
>> +/**
>> + * dpll_device_get - find or create dpll_device object
>> + * @clock_id: a system unique number for a device
>> + * @dev_driver_idx: 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,
>> +			 struct dpll_device_ops *ops, void *priv,
>> +			 struct device *owner);
>> +
>> +/**
>> + * dpll_device_unregister - deregister registered dpll
>> + * @dpll: pointer to dpll
>> + *
>> + * Unregister the dpll from the subsystem, make it unavailable for netlink
>> + * API users.
>> + */
>> +void dpll_device_unregister(struct dpll_device *dpll);
>> +
>> +/**
>> + * dpll_priv - get dpll private data
>> + * @dpll: pointer to dpll
>> + *
>> + * Obtain private data pointer passed to dpll subsystem when allocating
>> + * device with ``dpll_device_alloc(..)``
>> + */
>> +void *dpll_priv(const struct dpll_device *dpll);
>> +
>> +/**
>> + * dpll_pin_on_pin_priv - get pin on pin pair private data
>> + * @parent: pointer to a parent pin
>> + * @pin: pointer to a dpll_pin
>> + *
>> + * Obtain private pin data pointer passed to dpll subsystem when pin
>> + * was registered with parent pin.
>> + */
>> +void *dpll_pin_on_pin_priv(const struct dpll_pin *parent, const struct dpll_pin *pin);
>> +
>> +/**
>> + * dpll_pin_on_dpll_priv - get pin on dpll pair private data
>> + * @dpll: pointer to dpll
>> + * @pin: pointer to a dpll_pin
>> + *
>> + * Obtain private pin-dpll pair data pointer passed to dpll subsystem when pin
>> + * was registered with a dpll.
>> + */
>> +void *dpll_pin_on_dpll_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>> +
>> +/**
>> + * dpll_pin_get - get reference or create new pin object
>> + * @clock_id: a system unique number of a device
>> + * @dev_driver_idx: 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_idx 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,
> 
> Name inconsistency: dev_driver_id/idx in comment
> 
> 
>> +	      const struct dpll_pin_properties *pin_prop);
> 
> In .c you call this "prop", be consistent.
> 

Fixed both.

> 
>> +
>> +/**
>> + * 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,
>> +		      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
>> + *
>> + * Deregister previously registered pin object from a dpll device.
>> + *
>> + * Return:
>> + * * 0 - pin was successfully deregistered from this dpll device,
>> + * * -ENXIO - given pin was not registered with this dpll device,
>> + * * -EINVAL - pin pointer is not valid.
>> + */
>> +int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
>> +
>> +/**
>> + * 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,
>> +			     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);
>> +
>> +/**
>> + * 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);
>> +
>> +
>> +#endif
>> -- 
>> 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] 202+ messages in thread

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-03-13 22:59       ` Vadim Fedorenko
  (?)
@ 2023-03-14  9:21       ` Jiri Pirko
  2023-03-14 17:50           ` Kubalewski, Arkadiusz
  -1 siblings, 1 reply; 202+ messages in thread
From: Jiri Pirko @ 2023-03-14  9:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jakub Kicinski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Milena Olech, Michal Michalik

Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>On 13.03.2023 16:21, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>> > diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>> > new file mode 100644
>> > index 000000000000..d3926f2a733d
>> > --- /dev/null
>> > +++ b/drivers/dpll/Makefile
>> > @@ -0,0 +1,10 @@
>> > +# SPDX-License-Identifier: GPL-2.0
>> > +#
>> > +# Makefile for DPLL drivers.
>> > +#
>> > +
>> > +obj-$(CONFIG_DPLL)          += dpll_sys.o
>> 
>> What's "sys" and why is it here?
>
>It's an object file for the subsystem. Could be useful if we will have drivers
>for DPLL-only devices.

Yeah, but why "sys"? I don't get what "sys" means here.
Can't this be just "dpll.o"?


>
>> > +dpll_sys-y                  += dpll_core.o
>> > +dpll_sys-y                  += dpll_netlink.o
>> > +dpll_sys-y                  += dpll_nl.o
>> > +

[...]


>> > +struct dpll_device *
>> > +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module)
>> > +{
>> > +	struct dpll_device *dpll, *ret = NULL;
>> > +	unsigned long index;
>> > +
>> > +	mutex_lock(&dpll_device_xa_lock);
>> > +	xa_for_each(&dpll_device_xa, index, dpll) {
>> > +		if (dpll->clock_id == clock_id &&
>> > +		    dpll->dev_driver_id == dev_driver_id &&
>> 
>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>> identification, isn't that enough for you.
>
>dev_driver_id is needed to provide several DPLLs from one device. In ice driver
>implementation there are 2 different DPLLs - to recover from PPS input and to
>recover from Sync-E. I believe there is only one clock, that's why clock id
>is the same for both of them. But Arkadiusz can tell more about it.

Okay, I see. Clock_id is the same. Could we have index for pin, could
this be index too:

dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
	     const struct dpll_pin_properties *prop);

This way it is consistent, driver provides custom index for both dpll
device and dpll pin.

Makes sense?


>> 
>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>
>Simply id doesn't tell anything either. dpll_dev_id?

Yeah, see above.


>
>> > +		    dpll->module == module) {
>> > +			ret = dpll;
>> > +			refcount_inc(&ret->refcount);
>> > +			break;
>> > +		}
>> > +	}
>> > +	if (!ret)
>> > +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>> > +	mutex_unlock(&dpll_device_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;
>> 
>> Remove this check. The driver should not call this with NULL.
>
>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.

IDK, maybe for historical reasons. My point is, id driver is callin
this with NULL, there is something odd in the driver flow. Lets not
allow that for new code.


>
>> > +	mutex_lock(&dpll_device_xa_lock);
>> > +	if (refcount_dec_and_test(&dpll->refcount)) {
>> > +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>> 
>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>
>Good point!
>
>> > +		xa_destroy(&dpll->pin_refs);
>> > +		xa_erase(&dpll_device_xa, dpll->id);
>> > +		kfree(dpll);
>> > +	}
>> > +	mutex_unlock(&dpll_device_xa_lock);
>> > +}
>> > +EXPORT_SYMBOL_GPL(dpll_device_put);
>> > +
>> > +/**
>> > + * 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,
>> > +			 struct dpll_device_ops *ops, void *priv,
>> > +			 struct device *owner)
>> > +{
>> > +	if (WARN_ON(!ops || !owner))
>> > +		return -EINVAL;
>> > +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>> > +		return -EINVAL;
>> > +	mutex_lock(&dpll_device_xa_lock);
>> > +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>> > +		mutex_unlock(&dpll_device_xa_lock);
>> > +		return -EEXIST;
>> > +	}
>> > +	dpll->dev.bus = owner->bus;
>> > +	dpll->parent = owner;
>> > +	dpll->type = type;
>> > +	dpll->ops = ops;
>> > +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>> > +		     dpll->dev_driver_id);
>> 
>> This is really odd. As a result, the user would see something like:
>> pci/0000:01:00.0_1
>> pci/0000:01:00.0_2
>> 
>> I have to say it is confusing. In devlink, is bus/name and the user
>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>> there. Also, "_" might have some meaning on some bus. Should not
>> concatename dev_name() with anything.
>> 
>> Thinking about this some more, the module/clock_id tuple should be
>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>> as the user handle, can't it?
>> Example:
>> ice/c92d02a7129f4747
>> mlx5/90265d8bf6e6df56
>> 
>> If you really need the "dev_driver_id" (as I believe clock_id should be
>> enough), you can put it here as well:
>> ice/c92d02a7129f4747/1
>> ice/c92d02a7129f4747/2
>> 
>
>Looks good, will change it

Great.


>
>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>> share instance of DPLL equally, there is no "one clock master". >

[...]


>> > +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>> > +	if (!pin->prop.description) {
>> > +		ret = -ENOMEM;
>> > +		goto release;
>> > +	}
>> > +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>> > +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>> > +		ret = -EINVAL;
>> > +		goto release;
>> > +	}
>> > +	pin->prop.type = prop->type;
>> > +	pin->prop.capabilities = prop->capabilities;
>> > +	pin->prop.freq_supported = prop->freq_supported;
>> > +	pin->prop.any_freq_min = prop->any_freq_min;
>> > +	pin->prop.any_freq_max = prop->any_freq_max;
>> 
>> Make sure that the driver maintains prop (static const) and just save
>> the pointer. Prop does not need to be something driver needs to change.
>> 
>
>What's the difference? For ptp_ocp, we have the same configuration for all
>ext pins and the allocator only changes the name of the pin. Properties of
>the DPLL pins are stored within the pin object, not the driver, in this case.
>Not sure if the pointer way is much better...

For things like this it is common to have static const array in the
driver, like:

static const struct dpll_pin_properties dpll_pin_props[] = {
	{
		.description = "SMA0",
		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
		.type = DPLL_PIN_TYPE_EXT,
		.any_freq_max = 10000000,
		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
	},
	{
		.description = "SMA1",
		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
		.type = DPLL_PIN_TYPE_EXT,
		.any_freq_max = 10000000,
		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
	},
	{
		.description = "SMA2",
		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
		.type = DPLL_PIN_TYPE_EXT,
		.any_freq_max = 10000000,
		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
	},
	{
		.description = "SMA3",
		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
		.type = DPLL_PIN_TYPE_EXT,
		.any_freq_max = 10000000,
		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
	},
};

Here you have very nice list of pins, the reader knows right away what
is happening.

Thinking about "description" name, I think would be more appropriate to
name this "label" as it represents user-facing label on the connector,
isn't it? Does not describe anything.


>
>> 

[...]


>
>> 
>> > +	     const struct dpll_pin_properties *prop)
>> > +{
>> > +	struct dpll_pin *pos, *ret = NULL;
>> > +	unsigned long index;
>> > +
>> > +	mutex_lock(&dpll_pin_xa_lock);
>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>> > +		if (pos->clock_id == clock_id &&
>> > +		    pos->dev_driver_id == device_drv_id &&
>> > +		    pos->module == module) {
>> 
>> Compare prop as well.
>> 
>> Can't the driver_id (pin index) be something const as well? I think it
>> should. And therefore it could be easily put inside.
>> 
>
>I think clock_id + dev_driver_id + module should identify the pin exactly.
>And now I think that *prop is not needed here at all. Arkadiusz, any
>thoughts?

IDK, no strong opinion on this. I just thought it may help to identify
the pin and avoid potential driver bugs. (Like registering 2 pins with
the same properties).

[...]


>> > +/**
>> > + * 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,
>> > +		  struct dpll_pin_ops *ops, void *priv,
>> > +		  struct device *rclk_device)
>> 
>> Wait a second, what is this "struct device *"? Looks very odd.
>> 
>> 
>> > +{
>> > +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>> 
>> If you need to store something here, store the pointer to the device
>> directly. But this rclk_device seems odd to me.
>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>> is incomplete. What should it server for?
>> 
>
>Well, these questions go to Arkadiusz...

Okay.

[...]


>> > + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>> > + * @dpll: dpll device pointer
>> > + * @idx: index of pin
>> > + *
>> > + * Find a reference to a pin registered with given dpll and return its pointer.
>> > + *
>> > + * Return:
>> > + * * valid pointer if pin was found
>> > + * * NULL if not found
>> > + */
>> > +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>> > +{
>> > +	struct dpll_pin_ref *pos;
>> > +	unsigned long i;
>> > +
>> > +	xa_for_each(&dpll->pin_refs, i, pos) {
>> > +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>> 
>> How exactly pos->pin could be NULL?
>> 
>> Also, you are degrading the xarray to a mere list here with lookup like
>> this. Why can't you use the pin index coming from driver and
>> insert/lookup based on this index?
>> 
>Good point. We just have to be sure, that drivers provide 0-based indexes for
>their pins. I'll re-think it.

No, driver can provide indexing which is completely up to his decision.
You should use xa_insert() to insert the entry at specific index. See
devl_port_register for inspiration where it is done exactly like this.

And this should be done in exactly the same way for both pin and device.


>
>
>> 
>> > +			return pos->pin;
>> > +	}
>> > +
>> > +	return NULL;
>> > +}
>> > +
>> > +/**
>> > + * 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)
>> > +{
>> > +	return dpll->priv;
>> > +}
>> > +EXPORT_SYMBOL_GPL(dpll_priv);
>> > +
>> > +/**
>> > + * 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,
>> 
>> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
>> Just pass the priv directly to the op and avoid need for this helper,
>> no? Same goes to the rest of the priv helpers.

Did you miss this?


>> 
>> 
>> > +			    const struct dpll_pin *pin)
>> > +{
>> > +	struct dpll_pin_ref *ref;
>> > +
>> > +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
>> 
>> Why cast is needed here? You have this on multiple places.

Did you miss this?


>> 
>> 
>> > +	if (!ref)
>> > +		return NULL;
>> > +
>> > +	return ref->priv;
>> > +}
>> > +EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_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_ref *ref;
>> > +
>> > +	ref = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
>> > +	if (!ref)
>> > +		return NULL;
>> > +
>> > +	return ref->priv;
>> > +}
>> > +EXPORT_SYMBOL_GPL(dpll_pin_on_pin_priv);
>> > +
>> > +static int __init dpll_init(void)
>> > +{
>> > +	int ret;
>> > +
>> > +	ret = dpll_netlink_init();
>> > +	if (ret)
>> > +		goto error;
>> > +
>> > +	ret = class_register(&dpll_class);
>> 
>> Why exactly do you need this? I asked to remove this previously, IIRC
>> you said you would check if this needed. Why?
>> 
>Ah, sorry. Removed it now.

Np, good.

[...]


>> > +static int
>> > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
>> > +{
>> > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>> 
>> Why exactly do we need this dua--handle scheme? Why do you need
>> unpredictable DPLL_A_ID to be exposed to userspace?
>> It's just confusing.
>> 
>To be able to work with DPLL per integer after iterator on the list deducts
>which DPLL device is needed. It can reduce the amount of memory copies and
>simplify comparisons. Not sure why it's confusing.

Wait, I don't get it. Could you please explain a bit more?

My point is, there should be not such ID exposed over netlink
You don't need to expose it to userspace. The user has well defined
handle as you agreed with above. For example:

ice/c92d02a7129f4747/1
ice/c92d02a7129f4747/2

This is shown in dpll device GET/DUMP outputs.
Also user passes it during SET operation:
$ dplltool set ice/c92d02a7129f4747/1 mode auto

Isn't that enough stable and nice?


>
>> 
>> > +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>> > +			 struct netlink_ext_ack *extack)
>> > +{
>> > +	struct dpll_pin_ref *ref = NULL;
>> 
>> Why this needs to be initialized?
>> 
>No need, fixed.
>
>
>> 
>> > +	enum dpll_pin_state state;
>> > +	struct nlattr *nest;
>> > +	unsigned long index;
>> > +	int ret;
>> > +
>> > +	xa_for_each(&pin->parent_refs, index, ref) {
>> > +		if (WARN_ON(!ref->ops->state_on_pin_get))
>> > +			return -EFAULT;
>> > +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>> > +				ref->pin->dev_driver_id)) {
>> > +			ret = -EMSGSIZE;
>> > +			goto nest_cancel;
>> > +		}
>> > +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>> > +			ret = -EMSGSIZE;
>> > +			goto nest_cancel;
>> > +		}
>> > +		nla_nest_end(msg, nest);
>> > +	}
>> 
>> How is this function different to dpll_msg_add_pin_parents()?
>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>> hard to follow for me :/
>> 
>> Did you get lost here as well? If yes, this needs some serious think
>> through :)
>> 
>
>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>relationship between these things?

Okay, Arkadiuzs?




>
>> 
>> > +
>> > +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>> > +			 struct dpll_device *dpll,
>> > +			 struct netlink_ext_ack *extack)
>> > +{
>> > +	struct dpll_pin_ref *ref;
>> > +	int ret;
>> > +
>> > +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>> 
>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>> code.
>> 
>
>I believe it's INDEX which is provided by the driver. I'll think about renaming,
>but suggestions are welcome.

Let's use "index" and "INDEX" internalla and in Netlink attr names as
well then.

[...]


>
>> 
>> > +	int rem, ret = -EINVAL;
>> > +	struct nlattr *a;
>> > +
>> > +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>> > +			  genlmsg_len(info->genlhdr), rem) {
>> 
>> This is odd. Why you iterace over attrs? Why don't you just access them
>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>> 
>
>I had some unknown crashes when I was using such access. I might have lost some
>checks, will try it again.

Odd, yet definitelly debuggable though :)

[...]


>> > +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>> > +{
>> > +	mutex_lock(&dpll_pin_xa_lock);
>> 
>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>> taking order is opposite.
>> 
>
>Now I see an ABBA deadlock here, as well as in function before. Not sure how to
>solve it here. Any thoughts?

Well, here you can just call dpll_pre_dumpit() before mutex_lock(&dpll_pin_xa_lock)
to take the locks in the same order.


>
>> 
>> > +
>> > +	return dpll_pre_dumpit(cb);
>> > +}
>> > +
>> > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>> > +{
>> > +	mutex_unlock(&dpll_pin_xa_lock);
>> > +
>> > +	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->dev_driver_id))
>> > +		return -EMSGSIZE;
>> 
>> I don't really understand why you are trying figure something new and
>> interesting with the change notifications. This object mix and random
>> attrs fillup is something very wrong and makes userspace completely
>> fuzzy about what it is getting. And yet it is so simple:
>> You have 2 objects, dpll and pin, please just have:
>> dpll_notify()
>> dpll_pin_notify()
>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>> No need for any smartness. Have this dumb and simple.
>> 
>> Think about it more as about "object-state-snapshot" than "atomic-change"
>
>But with full object-snapshot user space app will lose the information about
>what exactly has changed. The reason to have this event is to provide the
>attributes which have changed. Otherwise, the app should have full snapshot
>and
>compare all attributes to figure out changes and that's might not be great idea.

Wait, are you saying that the app is stateless? Could you provide
example use cases?

From what I see, the app managing dpll knows the state of the device and
pins, it monitors for the changes and saves new state with appropriate
reaction (might be some action or maybe just log entry).


>
>> 
>> > +
>> > +	switch (attr) {
>> > +	case DPLL_A_MODE:
>> > +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>> > +		break;
>> > +	case DPLL_A_SOURCE_PIN_IDX:
>> > +		ret = dpll_msg_add_source_pin_idx(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:
>> > +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>> > +		break;
>> > +	case DPLL_A_PIN_PRIO:
>> > +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> > +		if (!ref)
>> > +			return -EFAULT;
>> > +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>> > +		break;
>> > +	case DPLL_A_PIN_STATE:
>> > +		if (parent) {
>> > +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>> > +			if (!ref)
>> > +				return -EFAULT;
>> > +			if (!ref->ops || !ref->ops->state_on_pin_get)
>> > +				return -EOPNOTSUPP;
>> > +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>> > +							 NULL);
>> > +			if (ret)
>> > +				return ret;
>> > +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>> > +					parent->dev_driver_id))
>> > +				return -EMSGSIZE;
>> > +		} else {
>> > +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>> > +			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);
>> 
>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>> I have to be missing something.
>
>Changes might come from other places, but will affect the DPLL device and we
>have to notify users in this case.

I'm not sure I follow. There are 2 scenarios for change:
1) user originated - user issues set of something
2) driver originated - something changes in HW, driver propagates that

With what I suggest, both scenarios work of course. My point is, user
knows very well the objects: device and pin, he knows the format or
messages that are related to GET/DUMP/SET operations of both. The
notification should have the same format, that is all I say.

Btw, you can see devlink code for example how the notifications like
this are implemented and work.



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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	pin->dev_driver_id = device_drv_id;
>+	pin->clock_id = clock_id;
>+	pin->module = module;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->description)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>+	if (!pin->prop.description) {
>+		ret = -ENOMEM;
>+		goto release;
>+	}
>+	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>+		    prop->type > DPLL_PIN_TYPE_MAX)) {
>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.type = prop->type;
>+	pin->prop.capabilities = prop->capabilities;
>+	pin->prop.freq_supported = prop->freq_supported;
>+	pin->prop.any_freq_min = prop->any_freq_min;
>+	pin->prop.any_freq_max = prop->any_freq_max;
>+	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->idx, pin,
>+		       xa_limit_16b, GFP_KERNEL);
>+release:
>+	if (ret) {
>+		xa_destroy(&pin->dpll_refs);
>+		xa_destroy(&pin->parent_refs);
>+		kfree(pin->prop.description);
>+		kfree(pin->rclk_dev_name);
>+		kfree(pin);
>+		return ERR_PTR(ret);
>+	}

This is odd flow. Just return 0 before "release" and have the goto-label
dedicated to error path only.

>+
>+	return pin;
>+}

[...]

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-12  2:28   ` Vadim Fedorenko
  (?)
  (?)
@ 2023-03-14 10:05   ` Jiri Pirko
  2023-03-15  0:10       ` Vadim Fedorenko
  -1 siblings, 1 reply; 202+ messages in thread
From: Jiri Pirko @ 2023-03-14 10:05 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>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 | 209 ++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 200 insertions(+), 10 deletions(-)
>
>diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>--- a/drivers/ptp/ptp_ocp.c
>+++ b/drivers/ptp/ptp_ocp.c
>@@ -23,6 +23,8 @@
> #include <linux/mtd/mtd.h>
> #include <linux/nvmem-consumer.h>
> #include <linux/crc16.h>
>+#include <linux/dpll.h>
>+#include <uapi/linux/dpll.h>

Don't include UAPI directly. I'm pretty sure I had this comment earlier.


> 
> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>@@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
> 	bool	fixed_dir;
> 	bool	disabled;
> 	u8	default_fcn;
>+	struct dpll_pin *dpll_pin;
> };
> 
> struct ocp_attr_group {
>@@ -353,6 +356,7 @@ struct ptp_ocp {
> 	struct ptp_ocp_signal	signal[4];
> 	struct ptp_ocp_sma_connector sma[4];

It is quite customary to use defines for const numbers like this. Why
don't you do that in ptp_ocp?


> 	const struct ocp_sma_op *sma_op;
>+	struct dpll_device *dpll;
> };
> 
> #define OCP_REQ_TIMESTAMP	BIT(0)
>@@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
> }
> 
> static int
>-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
> {
> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>-	enum ptp_ocp_sma_mode mode;
>-	int val;
>-
>-	mode = sma->mode;
>-	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>-	if (val < 0)
>-		return val;
> 
> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
> 		return -EOPNOTSUPP;
>@@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
> 	return val;
> }
> 
>+static int
>+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+{
>+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>+	enum ptp_ocp_sma_mode mode;
>+	int val;
>+
>+	mode = sma->mode;
>+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>+	if (val < 0)
>+		return val;
>+
>+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>+}
>+
> static ssize_t
> sma1_store(struct device *dev, struct device_attribute *attr,
> 	   const char *buf, size_t count)
>@@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
> 	device_unregister(&bp->dev);
> }
> 
>+static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>+{
>+	int i;
>+
>+	for (i = 0; i < 4; i++) {
>+		if (bp->sma[i].dpll_pin == pin)
>+			return i;

Just pass &bp->sma[i] as a priv to dpll_pin_register().
and get that pointer directly in pin ops. No need for lookup and use of
sma_nr at all.


>+	}
>+	return -1;
>+}
>+
>+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>+				    enum dpll_lock_status *status,
>+				    struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);

No need to cast (void *), remove it.


>+	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,
>+				    u32 *idx, struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+
>+	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,
>+				    u32 *mode, struct netlink_ext_ack *extack)
>+{
>+	*mode = DPLL_MODE_AUTOMATIC;
>+

No need for empty line, I believe.


>+	return 0;
>+}
>+
>+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>+				    const enum dpll_mode mode,
>+				    struct netlink_ext_ack *extack)
>+{
>+	if (mode == DPLL_MODE_AUTOMATIC)
>+		return true;
>+
>+	return false;

Just:
	return mode == DPLL_MODE_AUTOMATIC;

>+}
>+
>+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>+				     const struct dpll_device *dpll,
>+				     enum dpll_pin_direction *direction,
>+				     struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>+
>+	if (sma_nr < 0)
>+		return -EINVAL;
>+
>+	*direction = bp->sma[sma_nr].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,
>+				     const struct dpll_device *dpll,
>+				     enum dpll_pin_direction direction,
>+				     struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>+	enum ptp_ocp_sma_mode mode;
>+
>+	if (sma_nr < 0)
>+		return -EINVAL;
>+
>+	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,
>+			      const struct dpll_device *dpll,
>+			      const u32 frequency,

Why you need const for u32?


>+			      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>+	int val = frequency == 10000000 ? 0 : 1;

This is weird. I believe you should handle unsupported frequencies
gracefully.


>+
>+	if (sma_nr < 0)
>+		return -EINVAL;
>+
>+

Avoid double empty lines.


>+	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
>+}
>+
>+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>+			      const struct dpll_device *dpll,
>+			      u32 *frequency,
>+			      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>+	u32 val;
>+
>+	if (sma_nr < 0)
>+		return -EINVAL;
>+
>+	val = bp->sma_op->get(bp, sma_nr);
>+	if (!val)
>+		*frequency = 1000000;
>+	else
>+		*frequency = 0;

I don't follow. How the frequency could be 0? Does that mean that the
pin is disabled? If yes, you should rather implement
.state_on_dpll_get
.state_on_dpll_set

and leave the frequency constant.


>+	return 0;
>+}
>+
>+static struct dpll_device_ops dpll_ops = {

const


>+	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>+	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,

Should be called "source_pin_get" and return (struct dpll_pin *)
On the driver api level, no reason to pass indexes. Work with objects.


>+	.mode_get = ptp_ocp_dpll_mode_get,
>+	.mode_supported = ptp_ocp_dpll_mode_supported,
>+};
>+
>+static struct dpll_pin_ops dpll_pins_ops = {

const


>+	.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 dpll_pin_properties prop;
> 	struct devlink *devlink;
>+	char sma[4] = "SMA0";

Won't fit. Just use:
char *sma = "SMA0"
to be safe


> 	struct ptp_ocp *bp;
>-	int err;
>+	int err, i;
>+	u64 clkid;
> 
> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
> 	if (!devlink) {
>@@ -4226,8 +4377,44 @@ 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 (!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;
>+
>+	prop.description = &sma[0];
>+	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>+	prop.type = DPLL_PIN_TYPE_EXT;
>+	prop.any_freq_max = 10000000;
>+	prop.any_freq_min = 0;
>+	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>+
>+	for (i = 0; i < 4; i++) {
>+		sma[3] = 0x31 + i;

Ugh. Just use the static const array as I suggested in the dpll patch
reply. Helps you avoid this sort of "magic".


>+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>+		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {

How it could be NULL?


>+			bp->sma[i].dpll_pin = NULL;

This would not be needed if the error path iterates over
indexes which were successul.


>+			goto out_dpll;
>+		}
>+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
>+		if (err)
>+			goto out_dpll;
>+	}
>+
>+	return 0;
>+out_dpll:
>+	for (i = 0; i < 4; i++) {
>+		if (bp->sma[i].dpll_pin)

You don't do unregister of pin here.


>+			dpll_pin_put(bp->sma[i].dpll_pin);
>+	}
>+	dpll_device_put(bp->dpll);
> out:
> 	ptp_ocp_detach(bp);
> out_disable:
>@@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
> 	struct devlink *devlink = priv_to_devlink(bp);
> 
>+	dpll_device_unregister(bp->dpll);
>+	dpll_device_put(bp->dpll);
> 	devlink_unregister(devlink);
> 	ptp_ocp_detach(bp);
> 	pci_disable_device(pdev);
>-- 
>2.34.1
>

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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-14 14:44     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-14 14:44 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
> drivers/dpll/dpll_nl.c                | 126 +++++++
> drivers/dpll/dpll_nl.h                |  42 +++
> include/uapi/linux/dpll.h             | 196 ++++++++++
> 4 files changed, 878 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..03e9f0e2d3d2
>--- /dev/null
>+++ b/Documentation/netlink/specs/dpll.yaml
>@@ -0,0 +1,514 @@
>+name: dpll
>+
>+doc: DPLL subsystem.
>+
>+definitions:
>+  -
>+    type: const
>+    name: temp-divider
>+    value: 10
>+  -
>+    type: const
>+    name: pin-freq-1-hz
>+    value: 1
>+  -
>+    type: const
>+    name: pin-freq-10-mhz
>+    value: 10000000
>+  -
>+    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 (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: enum
>+    name: pin-type
>+    doc: Enumerates 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-state
>+    doc: available pin modes
>+    entries:
>+      -
>+        name: unspec
>+        doc: unspecified value
>+      -
>+        name: connected
>+        doc: pin connected
>+      -
>+        name: disconnected
>+        doc: pin disconnected
>+    render-max: true
>+  -
>+    type: enum
>+    name: pin-direction
>+    doc: available pin direction
>+    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: enum
>+    name: mode

Could you please sort the enums so they are in the same order as the
attribute which carries them? That would also put the device and pin
enums together.


>+    doc: |
>+      working-modes a dpll can support, differentiate if and how dpll selects
>+      one of its sources to syntonize with it
>+    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: 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: 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
>+  -
>+    type: flags
>+    name: pin-caps
>+    doc: define capabilities of a pin
>+    entries:
>+      -
>+        name: direction-can-change
>+      -
>+        name: priority-can-change
>+      -
>+        name: state-can-change
>+
>+
>+attribute-sets:
>+  -
>+    name: dpll
>+    enum-name: dplla
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1
>+        multi-attr: true
>+        nested-attributes: device

What is this "device" and what is it good for? Smells like some leftover
and with the nested scheme looks quite odd.


>+      -
>+        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: source-pin-idx
>+        type: u32
>+      -
>+        name: lock-status
>+        type: u8
>+        enum: lock-status
>+      -
>+        name: temp

Could you put some comment regarding the divider here?


>+        type: s32
>+      -
>+        name: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin
>+        value: 12
>+      -
>+        name: pin-idx
>+        type: u32
>+      -
>+        name: pin-description
>+        type: string
>+      -
>+        name: pin-type
>+        type: u8
>+        enum: pin-type
>+      -
>+        name: pin-direction
>+        type: u8
>+        enum: pin-direction
>+      -
>+        name: pin-frequency
>+        type: u32
>+      -
>+        name: pin-frequency-supported
>+        type: u32
>+        multi-attr: true
>+      -
>+        name: pin-any-frequency-min
>+        type: u32
>+      -
>+        name: pin-any-frequency-max
>+        type: u32

These 2 attrs somehow overlap with pin-frequency-supported,
which is a bit confusing, I think that pin-frequency-supported
should carry all supported frequencies. How about to have something
like:
        name: pin-frequency-supported
        type: nest
	multi-attr: true
        nested-attributes: pin-frequency-range

and then:
      name: pin-frequency-range
      subset-of: dpll
      attributes:
        -
          name: pin-frequency-min
          type: u32
        -
          name: pin-frequency-max
          type: u32
          doc: should be put only to specify range when value differs
	       from pin-frequency-min

That could carry list of frequencies and ranges, in this case multiple
ones if possibly needed.



>+      -
>+        name: pin-prio
>+        type: u32
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-parent
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin
>+        value: 23

Value 23? What's this?
You have it specified for some attrs all over the place.
What is the reason for it?


>+      -
>+        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: source-pin-idx
>+        type: u32
>+      -
>+        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
>+        type: nest
>+        value: 12
>+        multi-attr: true
>+        nested-attributes: pin

This does not belong here.


>+      -
>+        name: pin-prio
>+        type: u32
>+        value: 21
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-dpll-caps
>+        type: u32
>+        value: 26

All these 3 do not belong here are well.



>+  -
>+    name: pin
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1
>+        multi-attr: true
>+        nested-attributes: device
>+      -
>+        name: pin-idx
>+        type: u32
>+        value: 13
>+      -
>+        name: pin-description
>+        type: string
>+      -
>+        name: pin-type
>+        type: u8
>+        enum: pin-type
>+      -
>+        name: pin-direction
>+        type: u8
>+        enum: pin-direction
>+      -
>+        name: pin-frequency
>+        type: u32
>+      -
>+        name: pin-frequency-supported
>+        type: u32
>+        multi-attr: true
>+      -
>+        name: pin-any-frequency-min
>+        type: u32
>+      -
>+        name: pin-any-frequency-max
>+        type: u32
>+      -
>+        name: pin-prio
>+        type: u32
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-parent
>+        type: nest
>+        multi-attr: true

Multiple parents? How is that supposed to work?


>+        nested-attributes: pin-parent
>+        value: 23
>+      -
>+        name: pin-rclk-device
>+        type: string
>+        value: 25
>+      -
>+        name: pin-dpll-caps
>+        type: u32

Missing "enum: "


>+  -
>+    name: pin-parent
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-state
>+        type: u8
>+        value: 22
>+        enum: pin-state
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+        value: 24
>+      -
>+        name: pin-rclk-device
>+        type: string

Yeah, as I wrote in the other email, this really smells to
have like a simple string like this. What is it supposed to be?


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

Shouldn't this be "device"?


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

"device" here as well?


>+      flags: [ admin-perm ]
>+
>+      do:
>+        pre: dpll-pre-doit
>+        post: dpll-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+            - mode

Hmm, shouldn't source-pin-index be here as well?

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

"pin"?


>+      flags: [ admin-perm ]
>+
>+      do:
>+        pre: dpll-pin-pre-doit
>+        post: dpll-pin-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+        reply:
>+          attributes:
>+            - pin
>+
>+      dump:
>+        pre: dpll-pin-pre-dumpit
>+        post: dpll-pin-post-dumpit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        reply:
>+          attributes:
>+            - pin
>+
>+    -
>+      name: pin-set
>+      doc: Set attributes of a target pin
>+      attribute-set: dpll

"pin"?


>+      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-parent-idx
>+            - pin-state
>+
>+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..099d1e30ca7c
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.c
>@@ -0,0 +1,126 @@
>+// SPDX-License-Identifier: 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),

Hmm, any idea why the generator does not put define name
here instead of "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_U32, },

4.3GHz would be the limit, isn't it future proof to make this rather u64?


>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },

This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
is last. I found that the order is not according to the enum in the yaml
operation definition. Perhaps the generator can sort this?


>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>+};
>+
>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[6] = {
>+	{
>+		.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..3a354dfb9a31
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.h
>@@ -0,0 +1,42 @@
>+/* SPDX-License-Identifier: 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..ece6fe685d08
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,196 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+/* 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
>+
>+#define DPLL_TEMP_DIVIDER	10

Some comment to this define would be nice.


>+#define DPLL_PIN_FREQ_1_HZ	1
>+#define DPLL_PIN_FREQ_10_MHZ	10000000

Have this as enum. Spell out "frequency" to be consistent with the
related attribute name.


>+
>+/**
>+ * 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 (or is in

"any valid source" perhaps?


>+ *   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)
>+};
>+
>+/**
>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes

For some of the enums, you say for what attribute it serves as a value
set, for some you don't. Please unify the approach. I think it is
valuable to say that for every enum.


>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>+ */
>+enum dpll_pin_state {
>+	DPLL_PIN_STATE_UNSPEC,
>+	DPLL_PIN_STATE_CONNECTED,
>+	DPLL_PIN_STATE_DISCONNECTED,
>+
>+	__DPLL_PIN_STATE_MAX,
>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>+};
>+
>+/**
>+ * enum dpll_pin_direction - available pin direction
>+ * @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)
>+};
>+
>+/**
>+ * enum dpll_mode - working-modes a dpll can support, differentiate if and how
>+ *   dpll selects one of its sources to syntonize with it
>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>+ */
>+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 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_SOURCE_PIN_IDX,
>+	DPLL_A_LOCK_STATUS,
>+	DPLL_A_TEMP,
>+	DPLL_A_CLOCK_ID,
>+	DPLL_A_TYPE,
>+	DPLL_A_PIN,
>+	DPLL_A_PIN_IDX,
>+	DPLL_A_PIN_DESCRIPTION,

I commented this name in the other email. This does not describe
anything. It is a label on a connector, isn't it? Why don't to call it
"label"?


>+	DPLL_A_PIN_TYPE,
>+	DPLL_A_PIN_DIRECTION,
>+	DPLL_A_PIN_FREQUENCY,
>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,

Drop "any". Not good for anything.


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

Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
enum name.

>+
>+	__DPLL_A_MAX,
>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>+};
>+
>+enum {
>+	DPLL_CMD_UNSPEC,
>+	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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
> drivers/dpll/dpll_nl.c                | 126 +++++++
> drivers/dpll/dpll_nl.h                |  42 +++
> include/uapi/linux/dpll.h             | 196 ++++++++++
> 4 files changed, 878 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..03e9f0e2d3d2
>--- /dev/null
>+++ b/Documentation/netlink/specs/dpll.yaml
>@@ -0,0 +1,514 @@
>+name: dpll
>+
>+doc: DPLL subsystem.
>+
>+definitions:
>+  -
>+    type: const
>+    name: temp-divider
>+    value: 10
>+  -
>+    type: const
>+    name: pin-freq-1-hz
>+    value: 1
>+  -
>+    type: const
>+    name: pin-freq-10-mhz
>+    value: 10000000
>+  -
>+    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 (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: enum
>+    name: pin-type
>+    doc: Enumerates 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-state
>+    doc: available pin modes
>+    entries:
>+      -
>+        name: unspec
>+        doc: unspecified value
>+      -
>+        name: connected
>+        doc: pin connected
>+      -
>+        name: disconnected
>+        doc: pin disconnected
>+    render-max: true
>+  -
>+    type: enum
>+    name: pin-direction
>+    doc: available pin direction
>+    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: enum
>+    name: mode

Could you please sort the enums so they are in the same order as the
attribute which carries them? That would also put the device and pin
enums together.


>+    doc: |
>+      working-modes a dpll can support, differentiate if and how dpll selects
>+      one of its sources to syntonize with it
>+    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: 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: 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
>+  -
>+    type: flags
>+    name: pin-caps
>+    doc: define capabilities of a pin
>+    entries:
>+      -
>+        name: direction-can-change
>+      -
>+        name: priority-can-change
>+      -
>+        name: state-can-change
>+
>+
>+attribute-sets:
>+  -
>+    name: dpll
>+    enum-name: dplla
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1
>+        multi-attr: true
>+        nested-attributes: device

What is this "device" and what is it good for? Smells like some leftover
and with the nested scheme looks quite odd.


>+      -
>+        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: source-pin-idx
>+        type: u32
>+      -
>+        name: lock-status
>+        type: u8
>+        enum: lock-status
>+      -
>+        name: temp

Could you put some comment regarding the divider here?


>+        type: s32
>+      -
>+        name: clock-id
>+        type: u64
>+      -
>+        name: type
>+        type: u8
>+        enum: type
>+      -
>+        name: pin
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin
>+        value: 12
>+      -
>+        name: pin-idx
>+        type: u32
>+      -
>+        name: pin-description
>+        type: string
>+      -
>+        name: pin-type
>+        type: u8
>+        enum: pin-type
>+      -
>+        name: pin-direction
>+        type: u8
>+        enum: pin-direction
>+      -
>+        name: pin-frequency
>+        type: u32
>+      -
>+        name: pin-frequency-supported
>+        type: u32
>+        multi-attr: true
>+      -
>+        name: pin-any-frequency-min
>+        type: u32
>+      -
>+        name: pin-any-frequency-max
>+        type: u32

These 2 attrs somehow overlap with pin-frequency-supported,
which is a bit confusing, I think that pin-frequency-supported
should carry all supported frequencies. How about to have something
like:
        name: pin-frequency-supported
        type: nest
	multi-attr: true
        nested-attributes: pin-frequency-range

and then:
      name: pin-frequency-range
      subset-of: dpll
      attributes:
        -
          name: pin-frequency-min
          type: u32
        -
          name: pin-frequency-max
          type: u32
          doc: should be put only to specify range when value differs
	       from pin-frequency-min

That could carry list of frequencies and ranges, in this case multiple
ones if possibly needed.



>+      -
>+        name: pin-prio
>+        type: u32
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-parent
>+        type: nest
>+        multi-attr: true
>+        nested-attributes: pin
>+        value: 23

Value 23? What's this?
You have it specified for some attrs all over the place.
What is the reason for it?


>+      -
>+        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: source-pin-idx
>+        type: u32
>+      -
>+        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
>+        type: nest
>+        value: 12
>+        multi-attr: true
>+        nested-attributes: pin

This does not belong here.


>+      -
>+        name: pin-prio
>+        type: u32
>+        value: 21
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-dpll-caps
>+        type: u32
>+        value: 26

All these 3 do not belong here are well.



>+  -
>+    name: pin
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: device
>+        type: nest
>+        value: 1
>+        multi-attr: true
>+        nested-attributes: device
>+      -
>+        name: pin-idx
>+        type: u32
>+        value: 13
>+      -
>+        name: pin-description
>+        type: string
>+      -
>+        name: pin-type
>+        type: u8
>+        enum: pin-type
>+      -
>+        name: pin-direction
>+        type: u8
>+        enum: pin-direction
>+      -
>+        name: pin-frequency
>+        type: u32
>+      -
>+        name: pin-frequency-supported
>+        type: u32
>+        multi-attr: true
>+      -
>+        name: pin-any-frequency-min
>+        type: u32
>+      -
>+        name: pin-any-frequency-max
>+        type: u32
>+      -
>+        name: pin-prio
>+        type: u32
>+      -
>+        name: pin-state
>+        type: u8
>+        enum: pin-state
>+      -
>+        name: pin-parent
>+        type: nest
>+        multi-attr: true

Multiple parents? How is that supposed to work?


>+        nested-attributes: pin-parent
>+        value: 23
>+      -
>+        name: pin-rclk-device
>+        type: string
>+        value: 25
>+      -
>+        name: pin-dpll-caps
>+        type: u32

Missing "enum: "


>+  -
>+    name: pin-parent
>+    subset-of: dpll
>+    attributes:
>+      -
>+        name: pin-state
>+        type: u8
>+        value: 22
>+        enum: pin-state
>+      -
>+        name: pin-parent-idx
>+        type: u32
>+        value: 24
>+      -
>+        name: pin-rclk-device
>+        type: string

Yeah, as I wrote in the other email, this really smells to
have like a simple string like this. What is it supposed to be?


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

Shouldn't this be "device"?


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

"device" here as well?


>+      flags: [ admin-perm ]
>+
>+      do:
>+        pre: dpll-pre-doit
>+        post: dpll-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+            - mode

Hmm, shouldn't source-pin-index be here as well?

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

"pin"?


>+      flags: [ admin-perm ]
>+
>+      do:
>+        pre: dpll-pin-pre-doit
>+        post: dpll-pin-post-doit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+            - pin-idx
>+        reply:
>+          attributes:
>+            - pin
>+
>+      dump:
>+        pre: dpll-pin-pre-dumpit
>+        post: dpll-pin-post-dumpit
>+        request:
>+          attributes:
>+            - id
>+            - bus-name
>+            - dev-name
>+        reply:
>+          attributes:
>+            - pin
>+
>+    -
>+      name: pin-set
>+      doc: Set attributes of a target pin
>+      attribute-set: dpll

"pin"?


>+      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-parent-idx
>+            - pin-state
>+
>+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..099d1e30ca7c
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.c
>@@ -0,0 +1,126 @@
>+// SPDX-License-Identifier: 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),

Hmm, any idea why the generator does not put define name
here instead of "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_U32, },

4.3GHz would be the limit, isn't it future proof to make this rather u64?


>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },

This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
is last. I found that the order is not according to the enum in the yaml
operation definition. Perhaps the generator can sort this?


>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>+};
>+
>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[6] = {
>+	{
>+		.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..3a354dfb9a31
>--- /dev/null
>+++ b/drivers/dpll/dpll_nl.h
>@@ -0,0 +1,42 @@
>+/* SPDX-License-Identifier: 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..ece6fe685d08
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,196 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+/* 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
>+
>+#define DPLL_TEMP_DIVIDER	10

Some comment to this define would be nice.


>+#define DPLL_PIN_FREQ_1_HZ	1
>+#define DPLL_PIN_FREQ_10_MHZ	10000000

Have this as enum. Spell out "frequency" to be consistent with the
related attribute name.


>+
>+/**
>+ * 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 (or is in

"any valid source" perhaps?


>+ *   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)
>+};
>+
>+/**
>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes

For some of the enums, you say for what attribute it serves as a value
set, for some you don't. Please unify the approach. I think it is
valuable to say that for every enum.


>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>+ */
>+enum dpll_pin_state {
>+	DPLL_PIN_STATE_UNSPEC,
>+	DPLL_PIN_STATE_CONNECTED,
>+	DPLL_PIN_STATE_DISCONNECTED,
>+
>+	__DPLL_PIN_STATE_MAX,
>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>+};
>+
>+/**
>+ * enum dpll_pin_direction - available pin direction
>+ * @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)
>+};
>+
>+/**
>+ * enum dpll_mode - working-modes a dpll can support, differentiate if and how
>+ *   dpll selects one of its sources to syntonize with it
>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>+ */
>+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 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_SOURCE_PIN_IDX,
>+	DPLL_A_LOCK_STATUS,
>+	DPLL_A_TEMP,
>+	DPLL_A_CLOCK_ID,
>+	DPLL_A_TYPE,
>+	DPLL_A_PIN,
>+	DPLL_A_PIN_IDX,
>+	DPLL_A_PIN_DESCRIPTION,

I commented this name in the other email. This does not describe
anything. It is a label on a connector, isn't it? Why don't to call it
"label"?


>+	DPLL_A_PIN_TYPE,
>+	DPLL_A_PIN_DIRECTION,
>+	DPLL_A_PIN_FREQUENCY,
>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,

Drop "any". Not good for anything.


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

Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
enum name.

>+
>+	__DPLL_A_MAX,
>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>+};
>+
>+enum {
>+	DPLL_CMD_UNSPEC,
>+	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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>diff --git a/MAINTAINERS b/MAINTAINERS
>index edd3d562beee..0222b19af545 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6289,6 +6289,15 @@ 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

Why "clock"? You don't mention "clock" anywhere else.

[...]


>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..3fc151e16751
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,835 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.

Why "class" ?

[...]


>+static int
>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))

This is odd. As I suggested in the yaml patch, better to treat all
supported frequencies the same, no matter if it is range or not. The you
don't need this weird bitfield.

You can have a macro to help driver to assemble array of supported
frequencies and ranges.


>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)

How exactly this can happen? Looks to me like only in case of a bug.
WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?


>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (pin->rclk_dev_name)

Use && and single if


>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>+		  struct netlink_ext_ack *extack)
>+{
>+	u32 freq = nla_get_u32(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) {
>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>+		if (ret)
>+			return -EFAULT;

return what the op returns: ret


>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>+	}
>+
>+	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) {
>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))

ret = ..
if (ret)
	return ret;

Please use this pattern in other ops call code as well.


>+			return -EFAULT;
>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>+	}
>+
>+	return 0;

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>diff --git a/MAINTAINERS b/MAINTAINERS
>index edd3d562beee..0222b19af545 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6289,6 +6289,15 @@ 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

Why "clock"? You don't mention "clock" anywhere else.

[...]


>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..3fc151e16751
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,835 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.

Why "class" ?

[...]


>+static int
>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))

This is odd. As I suggested in the yaml patch, better to treat all
supported frequencies the same, no matter if it is range or not. The you
don't need this weird bitfield.

You can have a macro to help driver to assemble array of supported
frequencies and ranges.


>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)

How exactly this can happen? Looks to me like only in case of a bug.
WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?


>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (pin->rclk_dev_name)

Use && and single if


>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>+				   pin->rclk_dev_name))
>+			return -EMSGSIZE;
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>+		  struct netlink_ext_ack *extack)
>+{
>+	u32 freq = nla_get_u32(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) {
>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>+		if (ret)
>+			return -EFAULT;

return what the op returns: ret


>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>+	}
>+
>+	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) {
>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))

ret = ..
if (ret)
	return ret;

Please use this pattern in other ops call code as well.


>+			return -EFAULT;
>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>+	}
>+
>+	return 0;

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-14 16:14     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-14 16:14 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
> Documentation/networking/index.rst |   1 +
> 2 files changed, 348 insertions(+)
> create mode 100644 Documentation/networking/dpll.rst
>
>diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
>new file mode 100644
>index 000000000000..25cd81edc73c
>--- /dev/null
>+++ b/Documentation/networking/dpll.rst
>@@ -0,0 +1,347 @@
>+.. 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.
>+
>+
>+Dpll object

rather perhaps "Device object"?


>+===========
>+Single dpll device object means single Digital PLL circuit and bunch of
>+pins connected with it.
>+It provides its capablities and current status to the user in response

Which capabilities you have in mind. There is CAPs for pins, but I see
none for device.


>+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 input or output, it

In the code and UAPI you use terms "source" and "output". Please align
to that.


>+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 properities and capabilities are provided to the user in response

s/properities/properties/

There is more provided, like "status" for example.


>+to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
>+It is also possible to list all the pins that were registered either
>+with dpll or different pin with `dump` request of ``DPLL_CMD_PIN_GET``

I don't follow. Dump of DPLL_CMD_PIN_GET just dumps always all the pins
in the system. Am I missing something?


>+command.
>+Configuration of a pin can be changed by `do` request of netlink
>+``DPLL_CMD_PIN_SET`` command.
>+
>+
>+Shared pins
>+===========
>+Pin can be shared by multiple dpll devices. Where configuration on one
>+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
>+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).

Perhaps this can be extended to something like this:

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, which aggregates child pins and serves as pin
>+multiplexer. One or more pins are attached to MUX-type instead of being
>+directly connected to a dpll device.

Perhaps you can say "registered" in stead of "connected" and "attached"
so this is aligned with the function names?


>+Pins registered with a MUX-type provides user with additional nested

s/provides/provide/


>+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered

s/parrent/parent/

I'm confused. Can one pin be registered to multiple parents? How is that
supposed to be working?


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

Tell the enum value.


>+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 are not capable of automatic source pin selection,

s/are not/is not/


>+in order to configure a source of a MUX-type pin the user still needs
>+to request desired pin state.

Perhaps emphasize that this is the state of the child?


>+
>+
>+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 command 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
>+    ``SOURCE_PIN_IDX``          attr index of currently selected source
>+    ``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

Ah, now I understand what this is about. Didn't occur to me from the
netlink UAPI :/


>+      ``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_PRIO``                attr pin priority to be set

I think it would be good to emhasize which attribute is valid in which
combination, meaning alone, dpll needs to be specified, parent pin needs
to be specified


>+    ``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. Possible response message attributes for netlink dump
>+requests:
>+
>+  ==============================  =======================================
>+  ``PIN_GET``                     command to dump pins

Maintain the order and start with DEVICE_GET 



>+    ``PIN``                       attr nested type contains single pin
>+      ``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_IDX``                 attr index of dumped pin (on dplls)
>+      ``PIN_DESCRIPTION``         description of a pin provided by driver
>+      ``PIN_TYPE``                attr value of pin type
>+      ``PIN_FREQUENCY``           attr current frequency of a pin
>+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
>+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
>+                                  recovers clock signal from
>+      ``PIN_DIRECTION``           attr direction of a pin
>+      ``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
>+
>+  ``DEVICE_GET``                  command to dump dplls
>+    ``DEVICE``                    attr nested type contatin a single
>+                                  dpll device
>+      ``ID``                      attr internal dpll device ID
>+      ``DEV_NAME``                attr dpll device name
>+      ``BUS_NAME``                attr dpll device bus name

Hmm, why you need to repeat this for dump? Just say the message format
is the same as for DO and you are done with it.


>+
>+
>+Dpll 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 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 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 used to provide pulse-per-second output
>+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
>+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
>+
>+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 can register a dpll device within dpll subsytem.

"Driver instance". Btw, I need to change it for mlx5. I will try to
implement it in coming days.


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

In this text, you use "dpll", "Dpll", "DPLL". Could you unify?



>+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 id) use ``dpll_device_get``

s/to id/to it/


>+to obtain proper dpll device pointer.
>+
>+The name od DPLL device is generated based on registerer provided device

s/name od/name of/


>+struct pointer and dev_driver_id value.
>+Name is in format: ``%s_%u`` witch arguments:
>+``dev_name(struct device *)`` - syscall on parent device struct
>+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
>+driver with ``dpll_device_notify`` corresponding attribute as a reason.
>+
>+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.

Not sure internal implementation details are necessary. Just say that
driver is free to implement a set of ops it supports, leave the rest not
implemented.


>+
>diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
>index 4ddcae33c336..6eb83a47cc2d 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	[flat|nested] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
> Documentation/networking/index.rst |   1 +
> 2 files changed, 348 insertions(+)
> create mode 100644 Documentation/networking/dpll.rst
>
>diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
>new file mode 100644
>index 000000000000..25cd81edc73c
>--- /dev/null
>+++ b/Documentation/networking/dpll.rst
>@@ -0,0 +1,347 @@
>+.. 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.
>+
>+
>+Dpll object

rather perhaps "Device object"?


>+===========
>+Single dpll device object means single Digital PLL circuit and bunch of
>+pins connected with it.
>+It provides its capablities and current status to the user in response

Which capabilities you have in mind. There is CAPs for pins, but I see
none for device.


>+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 input or output, it

In the code and UAPI you use terms "source" and "output". Please align
to that.


>+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 properities and capabilities are provided to the user in response

s/properities/properties/

There is more provided, like "status" for example.


>+to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
>+It is also possible to list all the pins that were registered either
>+with dpll or different pin with `dump` request of ``DPLL_CMD_PIN_GET``

I don't follow. Dump of DPLL_CMD_PIN_GET just dumps always all the pins
in the system. Am I missing something?


>+command.
>+Configuration of a pin can be changed by `do` request of netlink
>+``DPLL_CMD_PIN_SET`` command.
>+
>+
>+Shared pins
>+===========
>+Pin can be shared by multiple dpll devices. Where configuration on one
>+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
>+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).

Perhaps this can be extended to something like this:

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, which aggregates child pins and serves as pin
>+multiplexer. One or more pins are attached to MUX-type instead of being
>+directly connected to a dpll device.

Perhaps you can say "registered" in stead of "connected" and "attached"
so this is aligned with the function names?


>+Pins registered with a MUX-type provides user with additional nested

s/provides/provide/


>+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered

s/parrent/parent/

I'm confused. Can one pin be registered to multiple parents? How is that
supposed to be working?


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

Tell the enum value.


>+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 are not capable of automatic source pin selection,

s/are not/is not/


>+in order to configure a source of a MUX-type pin the user still needs
>+to request desired pin state.

Perhaps emphasize that this is the state of the child?


>+
>+
>+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 command 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
>+    ``SOURCE_PIN_IDX``          attr index of currently selected source
>+    ``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

Ah, now I understand what this is about. Didn't occur to me from the
netlink UAPI :/


>+      ``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_PRIO``                attr pin priority to be set

I think it would be good to emhasize which attribute is valid in which
combination, meaning alone, dpll needs to be specified, parent pin needs
to be specified


>+    ``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. Possible response message attributes for netlink dump
>+requests:
>+
>+  ==============================  =======================================
>+  ``PIN_GET``                     command to dump pins

Maintain the order and start with DEVICE_GET 



>+    ``PIN``                       attr nested type contains single pin
>+      ``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_IDX``                 attr index of dumped pin (on dplls)
>+      ``PIN_DESCRIPTION``         description of a pin provided by driver
>+      ``PIN_TYPE``                attr value of pin type
>+      ``PIN_FREQUENCY``           attr current frequency of a pin
>+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
>+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
>+                                  recovers clock signal from
>+      ``PIN_DIRECTION``           attr direction of a pin
>+      ``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
>+
>+  ``DEVICE_GET``                  command to dump dplls
>+    ``DEVICE``                    attr nested type contatin a single
>+                                  dpll device
>+      ``ID``                      attr internal dpll device ID
>+      ``DEV_NAME``                attr dpll device name
>+      ``BUS_NAME``                attr dpll device bus name

Hmm, why you need to repeat this for dump? Just say the message format
is the same as for DO and you are done with it.


>+
>+
>+Dpll 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 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 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 used to provide pulse-per-second output
>+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
>+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
>+
>+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 can register a dpll device within dpll subsytem.

"Driver instance". Btw, I need to change it for mlx5. I will try to
implement it in coming days.


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

In this text, you use "dpll", "Dpll", "DPLL". Could you unify?



>+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 id) use ``dpll_device_get``

s/to id/to it/


>+to obtain proper dpll device pointer.
>+
>+The name od DPLL device is generated based on registerer provided device

s/name od/name of/


>+struct pointer and dev_driver_id value.
>+Name is in format: ``%s_%u`` witch arguments:
>+``dev_name(struct device *)`` - syscall on parent device struct
>+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
>+driver with ``dpll_device_notify`` corresponding attribute as a reason.
>+
>+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.

Not sure internal implementation details are necessary. Just say that
driver is free to implement a set of ops it supports, leave the rest not
implemented.


>+
>diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
>index 4ddcae33c336..6eb83a47cc2d 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	[flat|nested] 202+ messages in thread

* RE: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-03-13 22:59       ` Vadim Fedorenko
@ 2023-03-14 16:43         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-14 16:43 UTC (permalink / raw)
  To: Vadim Fedorenko, Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, poros, mschmidt,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Sent: Tuesday, March 14, 2023 12:00 AM
>

[...]

>>> +
>>> +/**
>>> + * dpll_device_get - find existing or create new dpll device
>>> + * @clock_id: clock_id of creator
>>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>>> +{
>>> +	struct dpll_device *dpll, *ret = NULL;
>>> +	unsigned long index;
>>> +
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>> +		if (dpll->clock_id == clock_id &&
>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>
>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>> identification, isn't that enough for you.
>
>dev_driver_id is needed to provide several DPLLs from one device. In ice
>driver
>implementation there are 2 different DPLLs - to recover from PPS input and
>to
>recover from Sync-E. I believe there is only one clock, that's why clock id
>is
>the same for both of them. But Arkadiusz can tell more about it.

Yes, exactly.
One driver can have multiple dplls with the same clock id.
Actually dev_driver_id makes dpll objects unique.

>>
>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>
>Simply id doesn't tell anything either. dpll_dev_id?

Looks good to me.

>
>>> +		    dpll->module == module) {
>>> +			ret = dpll;
>>> +			refcount_inc(&ret->refcount);
>>> +			break;
>>> +		}
>>> +	}
>>> +	if (!ret)
>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>> +	mutex_unlock(&dpll_device_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;
>>
>> Remove this check. The driver should not call this with NULL.
>
>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>

I agree, IMHO it is better to have safety checks :)

>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>
>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>
>Good point!
>

Yes, great point!

>>> +		xa_destroy(&dpll->pin_refs);
>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>> +		kfree(dpll);
>>> +	}
>>> +	mutex_unlock(&dpll_device_xa_lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>> +
>>> +/**
>>> + * 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,
>>> +			 struct dpll_device_ops *ops, void *priv,
>>> +			 struct device *owner)
>>> +{
>>> +	if (WARN_ON(!ops || !owner))
>>> +		return -EINVAL;
>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>> +		return -EINVAL;
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>> +		mutex_unlock(&dpll_device_xa_lock);
>>> +		return -EEXIST;
>>> +	}
>>> +	dpll->dev.bus = owner->bus;
>>> +	dpll->parent = owner;
>>> +	dpll->type = type;
>>> +	dpll->ops = ops;
>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>> +		     dpll->dev_driver_id);
>>
>> This is really odd. As a result, the user would see something like:
>> pci/0000:01:00.0_1
>> pci/0000:01:00.0_2
>>
>> I have to say it is confusing. In devlink, is bus/name and the user
>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>> there. Also, "_" might have some meaning on some bus. Should not
>> concatename dev_name() with anything.
>>
>> Thinking about this some more, the module/clock_id tuple should be
>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>> as the user handle, can't it?
>> Example:
>> ice/c92d02a7129f4747
>> mlx5/90265d8bf6e6df56
>>
>> If you really need the "dev_driver_id" (as I believe clock_id should be
>> enough), you can put it here as well:
>> ice/c92d02a7129f4747/1
>> ice/c92d02a7129f4747/2
>>
>
>Looks good, will change it

Makes sense to me.

>
>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>> share instance of DPLL equally, there is no "one clock master". >
>>> +	dpll->priv = priv;
>>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>> +	mutex_unlock(&dpll_device_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
>>> + *
>>> + * Deregister device, make it unavailable for userspace.
>>> + * Note: It does not free the memory
>>> + */
>>> +void dpll_device_unregister(struct dpll_device *dpll)
>>> +{
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	ASSERT_DPLL_REGISTERED(dpll);
>>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>> +	mutex_unlock(&dpll_device_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
>>> + * @dev_driver_id: id 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
>>
>> Extra "*"'s
>
>Ok, I can re-format the comments across the files.

This is expected on enumerating return values in kernel-docs:
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#Return%20values

>
>>> + */
>>> +struct dpll_pin *
>>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module
>*module,
>>
>> Odd whitespace.
>>
>> Also, func should be static.
>>
>
>Fixed.
>
>>
>>> +	       const struct dpll_pin_properties *prop)
>>> +{
>>> +	struct dpll_pin *pin;
>>> +	int ret;
>>> +
>>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>>> +	if (!pin)
>>> +		return ERR_PTR(-ENOMEM);
>>> +	pin->dev_driver_id = device_drv_id;
>>
>> Name inconsistency: driver/drv
>> you have it on multiple places
>>
>
>Changed it every where, thanks for spotting.
>
>>
>>> +	pin->clock_id = clock_id;
>>> +	pin->module = module;
>>> +	refcount_set(&pin->refcount, 1);
>>> +	if (WARN_ON(!prop->description)) {
>>> +		ret = -EINVAL;
>>> +		goto release;
>>> +	}
>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>> +	if (!pin->prop.description) {
>>> +		ret = -ENOMEM;
>>> +		goto release;
>>> +	}
>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>> +		ret = -EINVAL;
>>> +		goto release;
>>> +	}
>>> +	pin->prop.type = prop->type;
>>> +	pin->prop.capabilities = prop->capabilities;
>>> +	pin->prop.freq_supported = prop->freq_supported;
>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>
>> Make sure that the driver maintains prop (static const) and just save
>> the pointer. Prop does not need to be something driver needs to change.
>>
>
>What's the difference? For ptp_ocp, we have the same configuration for all
>ext pins and the allocator only changes the name of the pin. Properties of
>the DPLL pins are stored within the pin object, not the driver, in this
>case.
>Not sure if the pointer way is much better...
>

I also don't feel it.
Dpll subsystem directly using memory of different driver doesn't look like a
great design.

>>
>>> +	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->idx, pin,
>>> +		       xa_limit_16b, GFP_KERNEL);
>>> +release:
>>> +	if (ret) {
>>> +		xa_destroy(&pin->dpll_refs);
>>> +		xa_destroy(&pin->parent_refs);
>>> +		kfree(pin->prop.description);
>>> +		kfree(pin->rclk_dev_name);
>>> +		kfree(pin);
>>> +		return ERR_PTR(ret);
>>> +	}
>>> +
>>> +	return pin;
>>> +}
>>> +
>>> +/**
>>> + * dpll_pin_get - find existing or create new dpll pin
>>> + * @clock_id: clock_id of creator
>>> + * @dev_driver_id: id 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
>>
>> This is one example, I'm pretty sure that there are others, when you
>> have text inconsistencies in func doc for the same function in .c and .h
>> Have it please only on one place. .c is the usual place.
>>
>
>Yep, will clear .h files.
>

There might be some issues, and certainly makes sense to have them in one
place.

>>
>>> + */
>>> +struct dpll_pin *
>>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
>>
>> Again, why do you need this device_drv_id? Clock id should be enough.
>>
>I explained the reason earlier, but the naming is fixed.

Yes, this device_drv_id is id of a pin not a dpll device id.
Maybe worth to rename it to make it more clear.

>
>>
>>> +	     const struct dpll_pin_properties *prop)
>>> +{
>>> +	struct dpll_pin *pos, *ret = NULL;
>>> +	unsigned long index;
>>> +
>>> +	mutex_lock(&dpll_pin_xa_lock);
>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>> +		if (pos->clock_id == clock_id &&
>>> +		    pos->dev_driver_id == device_drv_id &&
>>> +		    pos->module == module) {
>>
>> Compare prop as well.
>>
>> Can't the driver_id (pin index) be something const as well? I think it
>> should. And therefore it could be easily put inside.
>>
>
>I think clock_id + dev_driver_id + module should identify the pin exactly.

Yes, they should be unique for a single pin object, and enough for getting
pin reference.
Basically if driver would call twice this with different props, it would be
broken.
We could have props compared here as well.

>And
>now I think that *prop is not needed here at all. Arkadiusz, any thoughts?

As dpll_pin_alloc is having them assigned to a pin object, thus had to put it
here. Not sure how to solve it differently.
Jiri's suggestion to put dev_driver_id inside of pin props would also work
AFAIU.

[...]

>>> +
>>> +/**
>>> + * 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,
>>> +		  struct dpll_pin_ops *ops, void *priv,
>>> +		  struct device *rclk_device)
>>
>> Wait a second, what is this "struct device *"? Looks very odd.
>>
>>
>>> +{
>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>
>> If you need to store something here, store the pointer to the device
>> directly. But this rclk_device seems odd to me.
>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>> is incomplete. What should it server for?
>>
>
>Well, these questions go to Arkadiusz...
>

If pin is able to recover signal from some device this shall convey that
device struct pointer.
Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
attribute.
Sure we can have pointer to device and use dev_name (each do/dump) on netlink
part. But isn't it better to have the name ready to use there?

It might be incomplete only if one device would have some kind of access to
a different bus? I don't think it is valid use case.

Basically the driver will refer only to the devices handled by that driver,
which means if dpll is on some bus, also all the pins are there, didn't notice
the need to have bus here as well.

[...]

>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>> +{
>>> +	struct dpll_pin_ref *pos;
>>> +	unsigned long i;
>>> +
>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>
>> How exactly pos->pin could be NULL?
>>

I believe if proper synchronization in place it shall not be NULL, I left it
after fixing some issue with access to the pin that was already removed..

>> Also, you are degrading the xarray to a mere list here with lookup like
>> this. Why can't you use the pin index coming from driver and
>> insert/lookup based on this index?
>>
>Good point. We just have to be sure, that drivers provide 0-based indexes
>for their pins. I'll re-think it.
>

After quick thinking, it might be doable, storing pin being registered on
dpll->pin_refs under given by driver pin's dev_driver_idx or whatever it would
be named.

>
>>
>>> +			return pos->pin;
>>> +	}
>>> +
>>> +	return NULL;
>>> +}
>>> +
>>> +/**
>>> + * 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)
>>> +{
>>> +	return dpll->priv;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dpll_priv);
>>> +
>>> +/**
>>> + * 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,
>>
>> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
>> Just pass the priv directly to the op and avoid need for this helper,
>> no? Same goes to the rest of the priv helpers.
>>

No strong opinion, probably doable.

>>
>>> +			    const struct dpll_pin *pin)
>>> +{
>>> +	struct dpll_pin_ref *ref;
>>> +
>>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
>>
>> Why cast is needed here? You have this on multiple places.
>>

`const` of struct dpll_pin *pin makes a warning/error there, there is something
broken on xarray implementation of for_each I believe.

[...]

>>> +
>>> +static int
>>> +dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>> +			 struct netlink_ext_ack *extack)
>>> +{
>>> +	struct dpll_pin_ref *ref = NULL;
>>
>> Why this needs to be initialized?
>>
>No need, fixed.
>
>
>>
>>> +	enum dpll_pin_state state;
>>> +	struct nlattr *nest;
>>> +	unsigned long index;
>>> +	int ret;
>>> +
>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>> +			return -EFAULT;
>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>> +				ref->pin->dev_driver_id)) {
>>> +			ret = -EMSGSIZE;
>>> +			goto nest_cancel;
>>> +		}
>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>> +			ret = -EMSGSIZE;
>>> +			goto nest_cancel;
>>> +		}
>>> +		nla_nest_end(msg, nest);
>>> +	}
>>
>> How is this function different to dpll_msg_add_pin_parents()?
>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>> hard to follow for me :/
>>
>> Did you get lost here as well? If yes, this needs some serious think
>> through :)
>>
>
>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>relationship between these things?
>

No, it is just leftover I didn't catch, we can leave one function and use it in
both cases. Sorry about that, great catch!

>>
>>> +
>>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>> +			 struct dpll_device *dpll,
>>> +			 struct netlink_ext_ack *extack)
>>> +{
>>> +	struct dpll_pin_ref *ref;
>>> +	int ret;
>>> +
>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>
>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>> code.
>>
>
>I believe it's INDEX which is provided by the driver. I'll think about
>renaming,
>but suggestions are welcome.

Yes, confusing a bit, I agree we need a fix of this.
Isn't it that dpll has an INDEX assigned by dpll subsystem on its allocation
and pin have ID given by the driver?

[...]

>>> +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;
>>> +	u32 parent_idx = PIN_IDX_INVALID;
>>
>> You just need this PIN_IDX_INVALID define internally in this function,
>> change the flow to avoid a need for it.
>>
>
>I'll re-think it, thanks.
>
>>
>>> +	int rem, ret = -EINVAL;
>>> +	struct nlattr *a;
>>> +
>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>> +			  genlmsg_len(info->genlhdr), rem) {
>>
>> This is odd. Why you iterace over attrs? Why don't you just access them
>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>
>
>I had some unknown crashes when I was using such access. I might have lost
>some
>checks, will try it again.
>
>>
>>> +		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_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) {
>>
>> Again, change the flow to:
>> 	if (attrs[DPLL_A_PIN_STATE]) {
>>
>> and avoid need for this value set/check.
>>
>
>Yep, will try.

Yes, this shall work now, as long as there are no multiple nested attributes
coming from userspace.

[...]

>>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct
>>>sk_buff *skb,
>>> +			struct genl_info *info)
>>> +{
>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>> +	dpll_post_doit(ops, skb, info);
>>> +}
>>> +
>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>> +{
>>> +	mutex_lock(&dpll_pin_xa_lock);
>>
>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>> taking order is opposite.
>>
>
>Now I see an ABBA deadlock here, as well as in function before. Not sure
>how to
>solve it here. Any thoughts?
>

Not really, this is why it is there :(
A single global lock for whole dpll subsystem?

>>
>>> +
>>> +	return dpll_pre_dumpit(cb);
>>> +}
>>> +
>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>> +{
>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>> +
>>> +	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->dev_driver_id))
>>> +		return -EMSGSIZE;
>>
>> I don't really understand why you are trying figure something new and
>> interesting with the change notifications. This object mix and random
>> attrs fillup is something very wrong and makes userspace completely
>> fuzzy about what it is getting. And yet it is so simple:
>> You have 2 objects, dpll and pin, please just have:
>> dpll_notify()
>> dpll_pin_notify()
>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>> No need for any smartness. Have this dumb and simple.
>>
>> Think about it more as about "object-state-snapshot" than "atomic-change"
>
>But with full object-snapshot user space app will lose the information
>about
>what exactly has changed. The reason to have this event is to provide the
>attributes which have changed. Otherwise, the app should have full snapshot
>and
>compare all attributes to figure out changes and that's might not be great
>idea.
>

I agree that from functional perspective it is better to have on userspace a
reason of the notification.

[...]

Thank you,
Arkadiusz


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

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

>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Sent: Tuesday, March 14, 2023 12:00 AM
>

[...]

>>> +
>>> +/**
>>> + * dpll_device_get - find existing or create new dpll device
>>> + * @clock_id: clock_id of creator
>>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>>> +{
>>> +	struct dpll_device *dpll, *ret = NULL;
>>> +	unsigned long index;
>>> +
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>> +		if (dpll->clock_id == clock_id &&
>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>
>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>> identification, isn't that enough for you.
>
>dev_driver_id is needed to provide several DPLLs from one device. In ice
>driver
>implementation there are 2 different DPLLs - to recover from PPS input and
>to
>recover from Sync-E. I believe there is only one clock, that's why clock id
>is
>the same for both of them. But Arkadiusz can tell more about it.

Yes, exactly.
One driver can have multiple dplls with the same clock id.
Actually dev_driver_id makes dpll objects unique.

>>
>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>
>Simply id doesn't tell anything either. dpll_dev_id?

Looks good to me.

>
>>> +		    dpll->module == module) {
>>> +			ret = dpll;
>>> +			refcount_inc(&ret->refcount);
>>> +			break;
>>> +		}
>>> +	}
>>> +	if (!ret)
>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>> +	mutex_unlock(&dpll_device_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;
>>
>> Remove this check. The driver should not call this with NULL.
>
>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>

I agree, IMHO it is better to have safety checks :)

>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>
>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>
>Good point!
>

Yes, great point!

>>> +		xa_destroy(&dpll->pin_refs);
>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>> +		kfree(dpll);
>>> +	}
>>> +	mutex_unlock(&dpll_device_xa_lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>> +
>>> +/**
>>> + * 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,
>>> +			 struct dpll_device_ops *ops, void *priv,
>>> +			 struct device *owner)
>>> +{
>>> +	if (WARN_ON(!ops || !owner))
>>> +		return -EINVAL;
>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>> +		return -EINVAL;
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>> +		mutex_unlock(&dpll_device_xa_lock);
>>> +		return -EEXIST;
>>> +	}
>>> +	dpll->dev.bus = owner->bus;
>>> +	dpll->parent = owner;
>>> +	dpll->type = type;
>>> +	dpll->ops = ops;
>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>> +		     dpll->dev_driver_id);
>>
>> This is really odd. As a result, the user would see something like:
>> pci/0000:01:00.0_1
>> pci/0000:01:00.0_2
>>
>> I have to say it is confusing. In devlink, is bus/name and the user
>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>> there. Also, "_" might have some meaning on some bus. Should not
>> concatename dev_name() with anything.
>>
>> Thinking about this some more, the module/clock_id tuple should be
>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>> as the user handle, can't it?
>> Example:
>> ice/c92d02a7129f4747
>> mlx5/90265d8bf6e6df56
>>
>> If you really need the "dev_driver_id" (as I believe clock_id should be
>> enough), you can put it here as well:
>> ice/c92d02a7129f4747/1
>> ice/c92d02a7129f4747/2
>>
>
>Looks good, will change it

Makes sense to me.

>
>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>> share instance of DPLL equally, there is no "one clock master". >
>>> +	dpll->priv = priv;
>>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>> +	mutex_unlock(&dpll_device_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
>>> + *
>>> + * Deregister device, make it unavailable for userspace.
>>> + * Note: It does not free the memory
>>> + */
>>> +void dpll_device_unregister(struct dpll_device *dpll)
>>> +{
>>> +	mutex_lock(&dpll_device_xa_lock);
>>> +	ASSERT_DPLL_REGISTERED(dpll);
>>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>> +	mutex_unlock(&dpll_device_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
>>> + * @dev_driver_id: id 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
>>
>> Extra "*"'s
>
>Ok, I can re-format the comments across the files.

This is expected on enumerating return values in kernel-docs:
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#Return%20values

>
>>> + */
>>> +struct dpll_pin *
>>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module
>*module,
>>
>> Odd whitespace.
>>
>> Also, func should be static.
>>
>
>Fixed.
>
>>
>>> +	       const struct dpll_pin_properties *prop)
>>> +{
>>> +	struct dpll_pin *pin;
>>> +	int ret;
>>> +
>>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>>> +	if (!pin)
>>> +		return ERR_PTR(-ENOMEM);
>>> +	pin->dev_driver_id = device_drv_id;
>>
>> Name inconsistency: driver/drv
>> you have it on multiple places
>>
>
>Changed it every where, thanks for spotting.
>
>>
>>> +	pin->clock_id = clock_id;
>>> +	pin->module = module;
>>> +	refcount_set(&pin->refcount, 1);
>>> +	if (WARN_ON(!prop->description)) {
>>> +		ret = -EINVAL;
>>> +		goto release;
>>> +	}
>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>> +	if (!pin->prop.description) {
>>> +		ret = -ENOMEM;
>>> +		goto release;
>>> +	}
>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>> +		ret = -EINVAL;
>>> +		goto release;
>>> +	}
>>> +	pin->prop.type = prop->type;
>>> +	pin->prop.capabilities = prop->capabilities;
>>> +	pin->prop.freq_supported = prop->freq_supported;
>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>
>> Make sure that the driver maintains prop (static const) and just save
>> the pointer. Prop does not need to be something driver needs to change.
>>
>
>What's the difference? For ptp_ocp, we have the same configuration for all
>ext pins and the allocator only changes the name of the pin. Properties of
>the DPLL pins are stored within the pin object, not the driver, in this
>case.
>Not sure if the pointer way is much better...
>

I also don't feel it.
Dpll subsystem directly using memory of different driver doesn't look like a
great design.

>>
>>> +	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->idx, pin,
>>> +		       xa_limit_16b, GFP_KERNEL);
>>> +release:
>>> +	if (ret) {
>>> +		xa_destroy(&pin->dpll_refs);
>>> +		xa_destroy(&pin->parent_refs);
>>> +		kfree(pin->prop.description);
>>> +		kfree(pin->rclk_dev_name);
>>> +		kfree(pin);
>>> +		return ERR_PTR(ret);
>>> +	}
>>> +
>>> +	return pin;
>>> +}
>>> +
>>> +/**
>>> + * dpll_pin_get - find existing or create new dpll pin
>>> + * @clock_id: clock_id of creator
>>> + * @dev_driver_id: id 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
>>
>> This is one example, I'm pretty sure that there are others, when you
>> have text inconsistencies in func doc for the same function in .c and .h
>> Have it please only on one place. .c is the usual place.
>>
>
>Yep, will clear .h files.
>

There might be some issues, and certainly makes sense to have them in one
place.

>>
>>> + */
>>> +struct dpll_pin *
>>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
>>
>> Again, why do you need this device_drv_id? Clock id should be enough.
>>
>I explained the reason earlier, but the naming is fixed.

Yes, this device_drv_id is id of a pin not a dpll device id.
Maybe worth to rename it to make it more clear.

>
>>
>>> +	     const struct dpll_pin_properties *prop)
>>> +{
>>> +	struct dpll_pin *pos, *ret = NULL;
>>> +	unsigned long index;
>>> +
>>> +	mutex_lock(&dpll_pin_xa_lock);
>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>> +		if (pos->clock_id == clock_id &&
>>> +		    pos->dev_driver_id == device_drv_id &&
>>> +		    pos->module == module) {
>>
>> Compare prop as well.
>>
>> Can't the driver_id (pin index) be something const as well? I think it
>> should. And therefore it could be easily put inside.
>>
>
>I think clock_id + dev_driver_id + module should identify the pin exactly.

Yes, they should be unique for a single pin object, and enough for getting
pin reference.
Basically if driver would call twice this with different props, it would be
broken.
We could have props compared here as well.

>And
>now I think that *prop is not needed here at all. Arkadiusz, any thoughts?

As dpll_pin_alloc is having them assigned to a pin object, thus had to put it
here. Not sure how to solve it differently.
Jiri's suggestion to put dev_driver_id inside of pin props would also work
AFAIU.

[...]

>>> +
>>> +/**
>>> + * 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,
>>> +		  struct dpll_pin_ops *ops, void *priv,
>>> +		  struct device *rclk_device)
>>
>> Wait a second, what is this "struct device *"? Looks very odd.
>>
>>
>>> +{
>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>
>> If you need to store something here, store the pointer to the device
>> directly. But this rclk_device seems odd to me.
>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>> is incomplete. What should it server for?
>>
>
>Well, these questions go to Arkadiusz...
>

If pin is able to recover signal from some device this shall convey that
device struct pointer.
Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
attribute.
Sure we can have pointer to device and use dev_name (each do/dump) on netlink
part. But isn't it better to have the name ready to use there?

It might be incomplete only if one device would have some kind of access to
a different bus? I don't think it is valid use case.

Basically the driver will refer only to the devices handled by that driver,
which means if dpll is on some bus, also all the pins are there, didn't notice
the need to have bus here as well.

[...]

>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>> +{
>>> +	struct dpll_pin_ref *pos;
>>> +	unsigned long i;
>>> +
>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>
>> How exactly pos->pin could be NULL?
>>

I believe if proper synchronization in place it shall not be NULL, I left it
after fixing some issue with access to the pin that was already removed..

>> Also, you are degrading the xarray to a mere list here with lookup like
>> this. Why can't you use the pin index coming from driver and
>> insert/lookup based on this index?
>>
>Good point. We just have to be sure, that drivers provide 0-based indexes
>for their pins. I'll re-think it.
>

After quick thinking, it might be doable, storing pin being registered on
dpll->pin_refs under given by driver pin's dev_driver_idx or whatever it would
be named.

>
>>
>>> +			return pos->pin;
>>> +	}
>>> +
>>> +	return NULL;
>>> +}
>>> +
>>> +/**
>>> + * 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)
>>> +{
>>> +	return dpll->priv;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dpll_priv);
>>> +
>>> +/**
>>> + * 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,
>>
>> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
>> Just pass the priv directly to the op and avoid need for this helper,
>> no? Same goes to the rest of the priv helpers.
>>

No strong opinion, probably doable.

>>
>>> +			    const struct dpll_pin *pin)
>>> +{
>>> +	struct dpll_pin_ref *ref;
>>> +
>>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
>>
>> Why cast is needed here? You have this on multiple places.
>>

`const` of struct dpll_pin *pin makes a warning/error there, there is something
broken on xarray implementation of for_each I believe.

[...]

>>> +
>>> +static int
>>> +dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>> +			 struct netlink_ext_ack *extack)
>>> +{
>>> +	struct dpll_pin_ref *ref = NULL;
>>
>> Why this needs to be initialized?
>>
>No need, fixed.
>
>
>>
>>> +	enum dpll_pin_state state;
>>> +	struct nlattr *nest;
>>> +	unsigned long index;
>>> +	int ret;
>>> +
>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>> +			return -EFAULT;
>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>> +				ref->pin->dev_driver_id)) {
>>> +			ret = -EMSGSIZE;
>>> +			goto nest_cancel;
>>> +		}
>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>> +			ret = -EMSGSIZE;
>>> +			goto nest_cancel;
>>> +		}
>>> +		nla_nest_end(msg, nest);
>>> +	}
>>
>> How is this function different to dpll_msg_add_pin_parents()?
>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>> hard to follow for me :/
>>
>> Did you get lost here as well? If yes, this needs some serious think
>> through :)
>>
>
>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>relationship between these things?
>

No, it is just leftover I didn't catch, we can leave one function and use it in
both cases. Sorry about that, great catch!

>>
>>> +
>>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>> +			 struct dpll_device *dpll,
>>> +			 struct netlink_ext_ack *extack)
>>> +{
>>> +	struct dpll_pin_ref *ref;
>>> +	int ret;
>>> +
>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>
>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>> code.
>>
>
>I believe it's INDEX which is provided by the driver. I'll think about
>renaming,
>but suggestions are welcome.

Yes, confusing a bit, I agree we need a fix of this.
Isn't it that dpll has an INDEX assigned by dpll subsystem on its allocation
and pin have ID given by the driver?

[...]

>>> +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;
>>> +	u32 parent_idx = PIN_IDX_INVALID;
>>
>> You just need this PIN_IDX_INVALID define internally in this function,
>> change the flow to avoid a need for it.
>>
>
>I'll re-think it, thanks.
>
>>
>>> +	int rem, ret = -EINVAL;
>>> +	struct nlattr *a;
>>> +
>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>> +			  genlmsg_len(info->genlhdr), rem) {
>>
>> This is odd. Why you iterace over attrs? Why don't you just access them
>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>
>
>I had some unknown crashes when I was using such access. I might have lost
>some
>checks, will try it again.
>
>>
>>> +		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_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) {
>>
>> Again, change the flow to:
>> 	if (attrs[DPLL_A_PIN_STATE]) {
>>
>> and avoid need for this value set/check.
>>
>
>Yep, will try.

Yes, this shall work now, as long as there are no multiple nested attributes
coming from userspace.

[...]

>>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct
>>>sk_buff *skb,
>>> +			struct genl_info *info)
>>> +{
>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>> +	dpll_post_doit(ops, skb, info);
>>> +}
>>> +
>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>> +{
>>> +	mutex_lock(&dpll_pin_xa_lock);
>>
>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>> taking order is opposite.
>>
>
>Now I see an ABBA deadlock here, as well as in function before. Not sure
>how to
>solve it here. Any thoughts?
>

Not really, this is why it is there :(
A single global lock for whole dpll subsystem?

>>
>>> +
>>> +	return dpll_pre_dumpit(cb);
>>> +}
>>> +
>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>> +{
>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>> +
>>> +	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->dev_driver_id))
>>> +		return -EMSGSIZE;
>>
>> I don't really understand why you are trying figure something new and
>> interesting with the change notifications. This object mix and random
>> attrs fillup is something very wrong and makes userspace completely
>> fuzzy about what it is getting. And yet it is so simple:
>> You have 2 objects, dpll and pin, please just have:
>> dpll_notify()
>> dpll_pin_notify()
>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>> No need for any smartness. Have this dumb and simple.
>>
>> Think about it more as about "object-state-snapshot" than "atomic-change"
>
>But with full object-snapshot user space app will lose the information
>about
>what exactly has changed. The reason to have this event is to provide the
>attributes which have changed. Otherwise, the app should have full snapshot
>and
>compare all attributes to figure out changes and that's might not be great
>idea.
>

I agree that from functional perspective it is better to have on userspace a
reason of the notification.

[...]

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] 202+ messages in thread

* RE: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-03-14  9:21       ` Jiri Pirko
@ 2023-03-14 17:50           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-14 17:50 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 10:22 AM
>
>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>> > diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>> > new file mode 100644
>>> > index 000000000000..d3926f2a733d
>>> > --- /dev/null
>>> > +++ b/drivers/dpll/Makefile
>>> > @@ -0,0 +1,10 @@
>>> > +# SPDX-License-Identifier: GPL-2.0
>>> > +#
>>> > +# Makefile for DPLL drivers.
>>> > +#
>>> > +
>>> > +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>
>>> What's "sys" and why is it here?
>>
>>It's an object file for the subsystem. Could be useful if we will have
>>drivers
>>for DPLL-only devices.
>
>Yeah, but why "sys"? I don't get what "sys" means here.
>Can't this be just "dpll.o"?
>
>
>>
>>> > +dpll_sys-y                  += dpll_core.o
>>> > +dpll_sys-y                  += dpll_netlink.o
>>> > +dpll_sys-y                  += dpll_nl.o
>>> > +
>
>[...]
>
>
>>> > +struct dpll_device *
>>> > +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>> > *module)
>>> > +{
>>> > +	struct dpll_device *dpll, *ret = NULL;
>>> > +	unsigned long index;
>>> > +
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	xa_for_each(&dpll_device_xa, index, dpll) {
>>> > +		if (dpll->clock_id == clock_id &&
>>> > +		    dpll->dev_driver_id == dev_driver_id &&
>>>
>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>> identification, isn't that enough for you.
>>
>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>driver
>>implementation there are 2 different DPLLs - to recover from PPS input and
>>to
>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>is the same for both of them. But Arkadiusz can tell more about it.
>
>Okay, I see. Clock_id is the same. Could we have index for pin, could
>this be index too:
>
>dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>	     const struct dpll_pin_properties *prop);
>
>This way it is consistent, driver provides custom index for both dpll
>device and dpll pin.
>
>Makes sense?
>

IMHO, Yes this better shows the intentions.

>
>>>
>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>
>>Simply id doesn't tell anything either. dpll_dev_id?
>
>Yeah, see above.
>
>
>>
>>> > +		    dpll->module == module) {
>>> > +			ret = dpll;
>>> > +			refcount_inc(&ret->refcount);
>>> > +			break;
>>> > +		}
>>> > +	}
>>> > +	if (!ret)
>>> > +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>> > +	mutex_unlock(&dpll_device_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;
>>>
>>> Remove this check. The driver should not call this with NULL.
>>
>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>
>IDK, maybe for historical reasons. My point is, id driver is callin
>this with NULL, there is something odd in the driver flow. Lets not
>allow that for new code.
>
>
>>
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	if (refcount_dec_and_test(&dpll->refcount)) {
>>> > +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>
>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>
>>Good point!
>>
>>> > +		xa_destroy(&dpll->pin_refs);
>>> > +		xa_erase(&dpll_device_xa, dpll->id);
>>> > +		kfree(dpll);
>>> > +	}
>>> > +	mutex_unlock(&dpll_device_xa_lock);
>>> > +}
>>> > +EXPORT_SYMBOL_GPL(dpll_device_put);
>>> > +
>>> > +/**
>>> > + * 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,
>>> > +			 struct dpll_device_ops *ops, void *priv,
>>> > +			 struct device *owner)
>>> > +{
>>> > +	if (WARN_ON(!ops || !owner))
>>> > +		return -EINVAL;
>>> > +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>> > +		return -EINVAL;
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>> > +		mutex_unlock(&dpll_device_xa_lock);
>>> > +		return -EEXIST;
>>> > +	}
>>> > +	dpll->dev.bus = owner->bus;
>>> > +	dpll->parent = owner;
>>> > +	dpll->type = type;
>>> > +	dpll->ops = ops;
>>> > +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>> > +		     dpll->dev_driver_id);
>>>
>>> This is really odd. As a result, the user would see something like:
>>> pci/0000:01:00.0_1
>>> pci/0000:01:00.0_2
>>>
>>> I have to say it is confusing. In devlink, is bus/name and the user
>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>> there. Also, "_" might have some meaning on some bus. Should not
>>> concatename dev_name() with anything.
>>>
>>> Thinking about this some more, the module/clock_id tuple should be
>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>> as the user handle, can't it?
>>> Example:
>>> ice/c92d02a7129f4747
>>> mlx5/90265d8bf6e6df56
>>>
>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>> enough), you can put it here as well:
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>
>>Looks good, will change it
>
>Great.
>
>
>>
>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>> share instance of DPLL equally, there is no "one clock master". >
>
>[...]
>
>
>>> > +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>> > +	if (!pin->prop.description) {
>>> > +		ret = -ENOMEM;
>>> > +		goto release;
>>> > +	}
>>> > +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>> > +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>> > +		ret = -EINVAL;
>>> > +		goto release;
>>> > +	}
>>> > +	pin->prop.type = prop->type;
>>> > +	pin->prop.capabilities = prop->capabilities;
>>> > +	pin->prop.freq_supported = prop->freq_supported;
>>> > +	pin->prop.any_freq_min = prop->any_freq_min;
>>> > +	pin->prop.any_freq_max = prop->any_freq_max;
>>>
>>> Make sure that the driver maintains prop (static const) and just save
>>> the pointer. Prop does not need to be something driver needs to change.
>>>
>>
>>What's the difference? For ptp_ocp, we have the same configuration for all
>>ext pins and the allocator only changes the name of the pin. Properties of
>>the DPLL pins are stored within the pin object, not the driver, in this
>case.
>>Not sure if the pointer way is much better...
>
>For things like this it is common to have static const array in the
>driver, like:
>
>static const struct dpll_pin_properties dpll_pin_props[] = {
>	{
>		.description = "SMA0",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA1",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA2",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA3",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>};
>
>Here you have very nice list of pins, the reader knows right away what
>is happening.
>
>Thinking about "description" name, I think would be more appropriate to
>name this "label" as it represents user-facing label on the connector,
>isn't it? Does not describe anything.
>

"label" seems good.

>
>>
>>>
>
>[...]
>
>
>>
>>>
>>> > +	     const struct dpll_pin_properties *prop)
>>> > +{
>>> > +	struct dpll_pin *pos, *ret = NULL;
>>> > +	unsigned long index;
>>> > +
>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>> > +		if (pos->clock_id == clock_id &&
>>> > +		    pos->dev_driver_id == device_drv_id &&
>>> > +		    pos->module == module) {
>>>
>>> Compare prop as well.
>>>
>>> Can't the driver_id (pin index) be something const as well? I think it
>>> should. And therefore it could be easily put inside.
>>>
>>
>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>thoughts?
>
>IDK, no strong opinion on this. I just thought it may help to identify
>the pin and avoid potential driver bugs. (Like registering 2 pins with
>the same properties).
>

It would make most sense if pin_index would be a part of *prop.

>[...]
>
>
>>> > +/**
>>> > + * 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,
>>> > +		  struct dpll_pin_ops *ops, void *priv,
>>> > +		  struct device *rclk_device)
>>>
>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>
>>>
>>> > +{
>>> > +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>> >NULL;
>>>
>>> If you need to store something here, store the pointer to the device
>>> directly. But this rclk_device seems odd to me.
>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>> is incomplete. What should it server for?
>>>
>>
>>Well, these questions go to Arkadiusz...
>


[ copy paste my answer from previous response ]
If pin is able to recover signal from some device this shall convey that
device struct pointer.
Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
attribute.
Sure we can have pointer to device and use dev_name (each do/dump) on netlink
part. But isn't it better to have the name ready to use there?

It might be incomplete only if one device would have some kind of access to
a different bus? I don't think it is valid use case.

Basically the driver will refer only to the devices handled by that driver,
which means if dpll is on some bus, also all the pins are there, didn't notice
the need to have bus here as well.

>Okay.
>
>[...]
>
>
>>> > + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>> > + * @dpll: dpll device pointer
>>> > + * @idx: index of pin
>>> > + *
>>> > + * Find a reference to a pin registered with given dpll and return
>>> > its pointer.
>>> > + *
>>> > + * Return:
>>> > + * * valid pointer if pin was found
>>> > + * * NULL if not found
>>> > + */
>>> > +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>> > +{
>>> > +	struct dpll_pin_ref *pos;
>>> > +	unsigned long i;
>>> > +
>>> > +	xa_for_each(&dpll->pin_refs, i, pos) {
>>> > +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>
>>> How exactly pos->pin could be NULL?
>>>
>>> Also, you are degrading the xarray to a mere list here with lookup like
>>> this. Why can't you use the pin index coming from driver and
>>> insert/lookup based on this index?
>>>
>>Good point. We just have to be sure, that drivers provide 0-based indexes for
>>their pins. I'll re-think it.
>
>No, driver can provide indexing which is completely up to his decision.
>You should use xa_insert() to insert the entry at specific index. See
>devl_port_register for inspiration where it is done exactly like this.
>
>And this should be done in exactly the same way for both pin and device.
>

Yes, I agree seems doable and better then it is now.

[...]

>>> > +static int
>>> > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>> >*dpll)
>>> > +{
>>> > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>
>>> Why exactly do we need this dua--handle scheme? Why do you need
>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>> It's just confusing.
>>>
>>To be able to work with DPLL per integer after iterator on the list deducts
>>which DPLL device is needed. It can reduce the amount of memory copies and
>>simplify comparisons. Not sure why it's confusing.
>
>Wait, I don't get it. Could you please explain a bit more?
>
>My point is, there should be not such ID exposed over netlink
>You don't need to expose it to userspace. The user has well defined
>handle as you agreed with above. For example:
>
>ice/c92d02a7129f4747/1
>ice/c92d02a7129f4747/2
>
>This is shown in dpll device GET/DUMP outputs.
>Also user passes it during SET operation:
>$ dplltool set ice/c92d02a7129f4747/1 mode auto
>
>Isn't that enough stable and nice?
>

I agree with Vadim, this is rather to be used by a daemon tools, which
would get the index once, then could use it as long as device is there.

>
>>
>>>
>>> > +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>> > +			 struct netlink_ext_ack *extack)
>>> > +{
>>> > +	struct dpll_pin_ref *ref = NULL;
>>>
>>> Why this needs to be initialized?
>>>
>>No need, fixed.
>>
>>
>>>
>>> > +	enum dpll_pin_state state;
>>> > +	struct nlattr *nest;
>>> > +	unsigned long index;
>>> > +	int ret;
>>> > +
>>> > +	xa_for_each(&pin->parent_refs, index, ref) {
>>> > +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>> > +			return -EFAULT;
>>> > +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>> > +				ref->pin->dev_driver_id)) {
>>> > +			ret = -EMSGSIZE;
>>> > +			goto nest_cancel;
>>> > +		}
>>> > +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>> > +			ret = -EMSGSIZE;
>>> > +			goto nest_cancel;
>>> > +		}
>>> > +		nla_nest_end(msg, nest);
>>> > +	}
>>>
>>> How is this function different to dpll_msg_add_pin_parents()?
>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>> hard to follow for me :/
>>>
>>> Did you get lost here as well? If yes, this needs some serious think
>>> through :)
>>>
>>
>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>relationship between these things?
>

[ copy paste my answer from previous response ]
No, it is just leftover I didn't catch, we can leave one function and use it in
both cases. Sorry about that, great catch!

[...]

>>> > +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>
>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>> code.
>>>
>>
>>I believe it's INDEX which is provided by the driver. I'll think about
>>renaming,
>>but suggestions are welcome.
>
>Let's use "index" and "INDEX" internalla and in Netlink attr names as
>well then.
>

For me makes sense to have a common name instead of origin-based one.

>[...]
>
>
>>
>>>
>>> > +	int rem, ret = -EINVAL;
>>> > +	struct nlattr *a;
>>> > +
>>> > +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>> > +			  genlmsg_len(info->genlhdr), rem) {
>>>
>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>
>>
>>I had some unknown crashes when I was using such access. I might have lost
>>some checks, will try it again.
>
>Odd, yet definitelly debuggable though :)
>
>[...]
>
>
>>> > +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>> > +{
>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>
>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>> taking order is opposite.
>>>
>>
>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>how to
>>solve it here. Any thoughts?
>
>Well, here you can just call dpll_pre_dumpit() before
>mutex_lock(&dpll_pin_xa_lock)
>to take the locks in the same order.
>

This double lock doesn't really improve anything.
Any objections on having single mutex/lock for access the dpll subsystem?

>
>>
>>>
>>> > +
>>> > +	return dpll_pre_dumpit(cb);
>>> > +}
>>> > +
>>> > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>> > +{
>>> > +	mutex_unlock(&dpll_pin_xa_lock);
>>> > +
>>> > +	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-
>>> > dev_driver_id))
>>> > +		return -EMSGSIZE;
>>>
>>> I don't really understand why you are trying figure something new and
>>> interesting with the change notifications. This object mix and random
>>> attrs fillup is something very wrong and makes userspace completely
>>> fuzzy about what it is getting. And yet it is so simple:
>>> You have 2 objects, dpll and pin, please just have:
>>> dpll_notify()
>>> dpll_pin_notify()
>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>> No need for any smartness. Have this dumb and simple.
>>>
>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>
>>But with full object-snapshot user space app will lose the information about
>>what exactly has changed. The reason to have this event is to provide the
>>attributes which have changed. Otherwise, the app should have full snapshot
>>and
>>compare all attributes to figure out changes and that's might not be great
>>idea.
>
>Wait, are you saying that the app is stateless? Could you provide
>example use cases?
>
>From what I see, the app managing dpll knows the state of the device and
>pins, it monitors for the changes and saves new state with appropriate
>reaction (might be some action or maybe just log entry).
>

It depends on the use case, right? App developer having those information knows
what has changed, thus can react in a way it thinks is most suitable.
IMHO from user perspective it is good to have a notification which actually
shows it's reason, so proper flow could be assigned to handle the reaction.

>
>>
>>>
>>> > +
>>> > +	switch (attr) {
>>> > +	case DPLL_A_MODE:
>>> > +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>> > +		break;
>>> > +	case DPLL_A_SOURCE_PIN_IDX:
>>> > +		ret = dpll_msg_add_source_pin_idx(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:
>>> > +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>> > +		break;
>>> > +	case DPLL_A_PIN_PRIO:
>>> > +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>> > +		if (!ref)
>>> > +			return -EFAULT;
>>> > +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>> > +		break;
>>> > +	case DPLL_A_PIN_STATE:
>>> > +		if (parent) {
>>> > +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>> > +			if (!ref)
>>> > +				return -EFAULT;
>>> > +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>> > +				return -EOPNOTSUPP;
>>> > +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>> > +							 NULL);
>>> > +			if (ret)
>>> > +				return ret;
>>> > +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>> > +					parent->dev_driver_id))
>>> > +				return -EMSGSIZE;
>>> > +		} else {
>>> > +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>> > +			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);
>>>
>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>> I have to be missing something.
>>
>>Changes might come from other places, but will affect the DPLL device and we
>>have to notify users in this case.
>
>I'm not sure I follow. There are 2 scenarios for change:
>1) user originated - user issues set of something
>2) driver originated - something changes in HW, driver propagates that
>
>With what I suggest, both scenarios work of course. My point is, user
>knows very well the objects: device and pin, he knows the format or
>messages that are related to GET/DUMP/SET operations of both. The
>notification should have the same format, that is all I say.
>
>Btw, you can see devlink code for example how the notifications like
>this are implemented and work.
>

Devlink packs all the info into a netlink message and notifies with it, isn't
it that it has all the info "buffered" in its structures?
A dpll subsystem keeps only some of the info in its internal structures, but
for most of it it has to question the driver. It would do it multiple times
i.e. if there would be a change on a pin connected to multiple dpll devices.

With current approach the notifications are pretty light to the subsystem as
well as for the registered clients, and as you suggested with having info of
what actually changed the userspace could implement a stateless daemon right
away.

Thank you,
Arkadiusz

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

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

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 10:22 AM
>
>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>> > diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>> > new file mode 100644
>>> > index 000000000000..d3926f2a733d
>>> > --- /dev/null
>>> > +++ b/drivers/dpll/Makefile
>>> > @@ -0,0 +1,10 @@
>>> > +# SPDX-License-Identifier: GPL-2.0
>>> > +#
>>> > +# Makefile for DPLL drivers.
>>> > +#
>>> > +
>>> > +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>
>>> What's "sys" and why is it here?
>>
>>It's an object file for the subsystem. Could be useful if we will have
>>drivers
>>for DPLL-only devices.
>
>Yeah, but why "sys"? I don't get what "sys" means here.
>Can't this be just "dpll.o"?
>
>
>>
>>> > +dpll_sys-y                  += dpll_core.o
>>> > +dpll_sys-y                  += dpll_netlink.o
>>> > +dpll_sys-y                  += dpll_nl.o
>>> > +
>
>[...]
>
>
>>> > +struct dpll_device *
>>> > +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>> > *module)
>>> > +{
>>> > +	struct dpll_device *dpll, *ret = NULL;
>>> > +	unsigned long index;
>>> > +
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	xa_for_each(&dpll_device_xa, index, dpll) {
>>> > +		if (dpll->clock_id == clock_id &&
>>> > +		    dpll->dev_driver_id == dev_driver_id &&
>>>
>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>> identification, isn't that enough for you.
>>
>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>driver
>>implementation there are 2 different DPLLs - to recover from PPS input and
>>to
>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>is the same for both of them. But Arkadiusz can tell more about it.
>
>Okay, I see. Clock_id is the same. Could we have index for pin, could
>this be index too:
>
>dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>	     const struct dpll_pin_properties *prop);
>
>This way it is consistent, driver provides custom index for both dpll
>device and dpll pin.
>
>Makes sense?
>

IMHO, Yes this better shows the intentions.

>
>>>
>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>
>>Simply id doesn't tell anything either. dpll_dev_id?
>
>Yeah, see above.
>
>
>>
>>> > +		    dpll->module == module) {
>>> > +			ret = dpll;
>>> > +			refcount_inc(&ret->refcount);
>>> > +			break;
>>> > +		}
>>> > +	}
>>> > +	if (!ret)
>>> > +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>> > +	mutex_unlock(&dpll_device_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;
>>>
>>> Remove this check. The driver should not call this with NULL.
>>
>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>
>IDK, maybe for historical reasons. My point is, id driver is callin
>this with NULL, there is something odd in the driver flow. Lets not
>allow that for new code.
>
>
>>
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	if (refcount_dec_and_test(&dpll->refcount)) {
>>> > +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>
>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>
>>Good point!
>>
>>> > +		xa_destroy(&dpll->pin_refs);
>>> > +		xa_erase(&dpll_device_xa, dpll->id);
>>> > +		kfree(dpll);
>>> > +	}
>>> > +	mutex_unlock(&dpll_device_xa_lock);
>>> > +}
>>> > +EXPORT_SYMBOL_GPL(dpll_device_put);
>>> > +
>>> > +/**
>>> > + * 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,
>>> > +			 struct dpll_device_ops *ops, void *priv,
>>> > +			 struct device *owner)
>>> > +{
>>> > +	if (WARN_ON(!ops || !owner))
>>> > +		return -EINVAL;
>>> > +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>> > +		return -EINVAL;
>>> > +	mutex_lock(&dpll_device_xa_lock);
>>> > +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>> > +		mutex_unlock(&dpll_device_xa_lock);
>>> > +		return -EEXIST;
>>> > +	}
>>> > +	dpll->dev.bus = owner->bus;
>>> > +	dpll->parent = owner;
>>> > +	dpll->type = type;
>>> > +	dpll->ops = ops;
>>> > +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>> > +		     dpll->dev_driver_id);
>>>
>>> This is really odd. As a result, the user would see something like:
>>> pci/0000:01:00.0_1
>>> pci/0000:01:00.0_2
>>>
>>> I have to say it is confusing. In devlink, is bus/name and the user
>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>> there. Also, "_" might have some meaning on some bus. Should not
>>> concatename dev_name() with anything.
>>>
>>> Thinking about this some more, the module/clock_id tuple should be
>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>> as the user handle, can't it?
>>> Example:
>>> ice/c92d02a7129f4747
>>> mlx5/90265d8bf6e6df56
>>>
>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>> enough), you can put it here as well:
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>
>>Looks good, will change it
>
>Great.
>
>
>>
>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>> share instance of DPLL equally, there is no "one clock master". >
>
>[...]
>
>
>>> > +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>> > +	if (!pin->prop.description) {
>>> > +		ret = -ENOMEM;
>>> > +		goto release;
>>> > +	}
>>> > +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>> > +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>> > +		ret = -EINVAL;
>>> > +		goto release;
>>> > +	}
>>> > +	pin->prop.type = prop->type;
>>> > +	pin->prop.capabilities = prop->capabilities;
>>> > +	pin->prop.freq_supported = prop->freq_supported;
>>> > +	pin->prop.any_freq_min = prop->any_freq_min;
>>> > +	pin->prop.any_freq_max = prop->any_freq_max;
>>>
>>> Make sure that the driver maintains prop (static const) and just save
>>> the pointer. Prop does not need to be something driver needs to change.
>>>
>>
>>What's the difference? For ptp_ocp, we have the same configuration for all
>>ext pins and the allocator only changes the name of the pin. Properties of
>>the DPLL pins are stored within the pin object, not the driver, in this
>case.
>>Not sure if the pointer way is much better...
>
>For things like this it is common to have static const array in the
>driver, like:
>
>static const struct dpll_pin_properties dpll_pin_props[] = {
>	{
>		.description = "SMA0",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA1",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA2",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>	{
>		.description = "SMA3",
>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>		.type = DPLL_PIN_TYPE_EXT,
>		.any_freq_max = 10000000,
>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>	},
>};
>
>Here you have very nice list of pins, the reader knows right away what
>is happening.
>
>Thinking about "description" name, I think would be more appropriate to
>name this "label" as it represents user-facing label on the connector,
>isn't it? Does not describe anything.
>

"label" seems good.

>
>>
>>>
>
>[...]
>
>
>>
>>>
>>> > +	     const struct dpll_pin_properties *prop)
>>> > +{
>>> > +	struct dpll_pin *pos, *ret = NULL;
>>> > +	unsigned long index;
>>> > +
>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>> > +		if (pos->clock_id == clock_id &&
>>> > +		    pos->dev_driver_id == device_drv_id &&
>>> > +		    pos->module == module) {
>>>
>>> Compare prop as well.
>>>
>>> Can't the driver_id (pin index) be something const as well? I think it
>>> should. And therefore it could be easily put inside.
>>>
>>
>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>thoughts?
>
>IDK, no strong opinion on this. I just thought it may help to identify
>the pin and avoid potential driver bugs. (Like registering 2 pins with
>the same properties).
>

It would make most sense if pin_index would be a part of *prop.

>[...]
>
>
>>> > +/**
>>> > + * 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,
>>> > +		  struct dpll_pin_ops *ops, void *priv,
>>> > +		  struct device *rclk_device)
>>>
>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>
>>>
>>> > +{
>>> > +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>> >NULL;
>>>
>>> If you need to store something here, store the pointer to the device
>>> directly. But this rclk_device seems odd to me.
>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>> is incomplete. What should it server for?
>>>
>>
>>Well, these questions go to Arkadiusz...
>


[ copy paste my answer from previous response ]
If pin is able to recover signal from some device this shall convey that
device struct pointer.
Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
attribute.
Sure we can have pointer to device and use dev_name (each do/dump) on netlink
part. But isn't it better to have the name ready to use there?

It might be incomplete only if one device would have some kind of access to
a different bus? I don't think it is valid use case.

Basically the driver will refer only to the devices handled by that driver,
which means if dpll is on some bus, also all the pins are there, didn't notice
the need to have bus here as well.

>Okay.
>
>[...]
>
>
>>> > + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>> > + * @dpll: dpll device pointer
>>> > + * @idx: index of pin
>>> > + *
>>> > + * Find a reference to a pin registered with given dpll and return
>>> > its pointer.
>>> > + *
>>> > + * Return:
>>> > + * * valid pointer if pin was found
>>> > + * * NULL if not found
>>> > + */
>>> > +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>> > +{
>>> > +	struct dpll_pin_ref *pos;
>>> > +	unsigned long i;
>>> > +
>>> > +	xa_for_each(&dpll->pin_refs, i, pos) {
>>> > +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>
>>> How exactly pos->pin could be NULL?
>>>
>>> Also, you are degrading the xarray to a mere list here with lookup like
>>> this. Why can't you use the pin index coming from driver and
>>> insert/lookup based on this index?
>>>
>>Good point. We just have to be sure, that drivers provide 0-based indexes for
>>their pins. I'll re-think it.
>
>No, driver can provide indexing which is completely up to his decision.
>You should use xa_insert() to insert the entry at specific index. See
>devl_port_register for inspiration where it is done exactly like this.
>
>And this should be done in exactly the same way for both pin and device.
>

Yes, I agree seems doable and better then it is now.

[...]

>>> > +static int
>>> > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>> >*dpll)
>>> > +{
>>> > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>
>>> Why exactly do we need this dua--handle scheme? Why do you need
>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>> It's just confusing.
>>>
>>To be able to work with DPLL per integer after iterator on the list deducts
>>which DPLL device is needed. It can reduce the amount of memory copies and
>>simplify comparisons. Not sure why it's confusing.
>
>Wait, I don't get it. Could you please explain a bit more?
>
>My point is, there should be not such ID exposed over netlink
>You don't need to expose it to userspace. The user has well defined
>handle as you agreed with above. For example:
>
>ice/c92d02a7129f4747/1
>ice/c92d02a7129f4747/2
>
>This is shown in dpll device GET/DUMP outputs.
>Also user passes it during SET operation:
>$ dplltool set ice/c92d02a7129f4747/1 mode auto
>
>Isn't that enough stable and nice?
>

I agree with Vadim, this is rather to be used by a daemon tools, which
would get the index once, then could use it as long as device is there.

>
>>
>>>
>>> > +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>> > +			 struct netlink_ext_ack *extack)
>>> > +{
>>> > +	struct dpll_pin_ref *ref = NULL;
>>>
>>> Why this needs to be initialized?
>>>
>>No need, fixed.
>>
>>
>>>
>>> > +	enum dpll_pin_state state;
>>> > +	struct nlattr *nest;
>>> > +	unsigned long index;
>>> > +	int ret;
>>> > +
>>> > +	xa_for_each(&pin->parent_refs, index, ref) {
>>> > +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>> > +			return -EFAULT;
>>> > +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>> > +				ref->pin->dev_driver_id)) {
>>> > +			ret = -EMSGSIZE;
>>> > +			goto nest_cancel;
>>> > +		}
>>> > +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>> > +			ret = -EMSGSIZE;
>>> > +			goto nest_cancel;
>>> > +		}
>>> > +		nla_nest_end(msg, nest);
>>> > +	}
>>>
>>> How is this function different to dpll_msg_add_pin_parents()?
>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>> hard to follow for me :/
>>>
>>> Did you get lost here as well? If yes, this needs some serious think
>>> through :)
>>>
>>
>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>relationship between these things?
>

[ copy paste my answer from previous response ]
No, it is just leftover I didn't catch, we can leave one function and use it in
both cases. Sorry about that, great catch!

[...]

>>> > +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>
>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>> code.
>>>
>>
>>I believe it's INDEX which is provided by the driver. I'll think about
>>renaming,
>>but suggestions are welcome.
>
>Let's use "index" and "INDEX" internalla and in Netlink attr names as
>well then.
>

For me makes sense to have a common name instead of origin-based one.

>[...]
>
>
>>
>>>
>>> > +	int rem, ret = -EINVAL;
>>> > +	struct nlattr *a;
>>> > +
>>> > +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>> > +			  genlmsg_len(info->genlhdr), rem) {
>>>
>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>
>>
>>I had some unknown crashes when I was using such access. I might have lost
>>some checks, will try it again.
>
>Odd, yet definitelly debuggable though :)
>
>[...]
>
>
>>> > +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>> > +{
>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>
>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>> taking order is opposite.
>>>
>>
>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>how to
>>solve it here. Any thoughts?
>
>Well, here you can just call dpll_pre_dumpit() before
>mutex_lock(&dpll_pin_xa_lock)
>to take the locks in the same order.
>

This double lock doesn't really improve anything.
Any objections on having single mutex/lock for access the dpll subsystem?

>
>>
>>>
>>> > +
>>> > +	return dpll_pre_dumpit(cb);
>>> > +}
>>> > +
>>> > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>> > +{
>>> > +	mutex_unlock(&dpll_pin_xa_lock);
>>> > +
>>> > +	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-
>>> > dev_driver_id))
>>> > +		return -EMSGSIZE;
>>>
>>> I don't really understand why you are trying figure something new and
>>> interesting with the change notifications. This object mix and random
>>> attrs fillup is something very wrong and makes userspace completely
>>> fuzzy about what it is getting. And yet it is so simple:
>>> You have 2 objects, dpll and pin, please just have:
>>> dpll_notify()
>>> dpll_pin_notify()
>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>> No need for any smartness. Have this dumb and simple.
>>>
>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>
>>But with full object-snapshot user space app will lose the information about
>>what exactly has changed. The reason to have this event is to provide the
>>attributes which have changed. Otherwise, the app should have full snapshot
>>and
>>compare all attributes to figure out changes and that's might not be great
>>idea.
>
>Wait, are you saying that the app is stateless? Could you provide
>example use cases?
>
>From what I see, the app managing dpll knows the state of the device and
>pins, it monitors for the changes and saves new state with appropriate
>reaction (might be some action or maybe just log entry).
>

It depends on the use case, right? App developer having those information knows
what has changed, thus can react in a way it thinks is most suitable.
IMHO from user perspective it is good to have a notification which actually
shows it's reason, so proper flow could be assigned to handle the reaction.

>
>>
>>>
>>> > +
>>> > +	switch (attr) {
>>> > +	case DPLL_A_MODE:
>>> > +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>> > +		break;
>>> > +	case DPLL_A_SOURCE_PIN_IDX:
>>> > +		ret = dpll_msg_add_source_pin_idx(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:
>>> > +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>> > +		break;
>>> > +	case DPLL_A_PIN_PRIO:
>>> > +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>> > +		if (!ref)
>>> > +			return -EFAULT;
>>> > +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>> > +		break;
>>> > +	case DPLL_A_PIN_STATE:
>>> > +		if (parent) {
>>> > +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>> > +			if (!ref)
>>> > +				return -EFAULT;
>>> > +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>> > +				return -EOPNOTSUPP;
>>> > +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>> > +							 NULL);
>>> > +			if (ret)
>>> > +				return ret;
>>> > +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>> > +					parent->dev_driver_id))
>>> > +				return -EMSGSIZE;
>>> > +		} else {
>>> > +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>> > +			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);
>>>
>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>> I have to be missing something.
>>
>>Changes might come from other places, but will affect the DPLL device and we
>>have to notify users in this case.
>
>I'm not sure I follow. There are 2 scenarios for change:
>1) user originated - user issues set of something
>2) driver originated - something changes in HW, driver propagates that
>
>With what I suggest, both scenarios work of course. My point is, user
>knows very well the objects: device and pin, he knows the format or
>messages that are related to GET/DUMP/SET operations of both. The
>notification should have the same format, that is all I say.
>
>Btw, you can see devlink code for example how the notifications like
>this are implemented and work.
>

Devlink packs all the info into a netlink message and notifies with it, isn't
it that it has all the info "buffered" in its structures?
A dpll subsystem keeps only some of the info in its internal structures, but
for most of it it has to question the driver. It would do it multiple times
i.e. if there would be a change on a pin connected to multiple dpll devices.

With current approach the notifications are pretty light to the subsystem as
well as for the registered clients, and as you suggested with having info of
what actually changed the userspace could implement a stateless daemon right
away.

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] 202+ messages in thread

* RE: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-03-14 15:45     ` Jiri Pirko
@ 2023-03-14 18:35       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-14 18:35 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 4:45 PM
>
>[...]
>
>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index edd3d562beee..0222b19af545 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6289,6 +6289,15 @@ 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
>
>Why "clock"? You don't mention "clock" anywhere else.
>
>[...]
>
>
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..3fc151e16751
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,835 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ *  dpll_core.c - Generic DPLL Management class support.
>
>Why "class" ?
>
>[...]
>
>
>>+static int
>>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>>+{
>>+	enum dpll_pin_freq_supp fs;
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long i;
>>+	u32 freq;
>>+
>>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>>+		if (ref && ref->ops && ref->dpll)
>>+			break;
>>+	}
>>+	if (!ref || !ref->ops || !ref->dpll)
>>+		return -ENODEV;
>>+	if (!ref->ops->frequency_get)
>>+		return -EOPNOTSUPP;
>>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>>+		return -EFAULT;
>>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>>+		return -EMSGSIZE;
>>+	if (!dump_any_freq)
>>+		return 0;
>>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>+			    dpll_pin_freq_value[fs]))
>
>This is odd. As I suggested in the yaml patch, better to treat all
>supported frequencies the same, no matter if it is range or not. The you
>don't need this weird bitfield.
>
>You can have a macro to help driver to assemble array of supported
>frequencies and ranges.
>

I understand suggestion on yaml, but here I am confused.
How do they relate to the supported frequency passed between driver and dpll
subsystem?
This bitfield is not visible to the userspace, and sure probably adding macro
can be useful.

>
>>+				return -EMSGSIZE;
>>+		}
>>+	}
>>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>+				pin->prop.any_freq_min))
>>+			return -EMSGSIZE;
>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>+				pin->prop.any_freq_max))
>>+			return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>
>[...]
>
>
>>+static int
>>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>+			 struct dpll_device *dpll,
>>+			 struct netlink_ext_ack *extack)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	int ret;
>>+
>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>>+		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, extack);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>+	if (!ref)
>
>How exactly this can happen? Looks to me like only in case of a bug.
>WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?

Yes, makes sense.

>
>
>>+		return -EFAULT;
>>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>>+	if (ret)
>>+		return ret;
>>+	if (pin->rclk_dev_name)
>
>Use && and single if
>

Make sense to me.

>
>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>+				   pin->rclk_dev_name))
>>+			return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>
>[...]
>
>
>>+static int
>>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>>+		  struct netlink_ext_ack *extack)
>>+{
>>+	u32 freq = nla_get_u32(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) {
>>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>>+		if (ret)
>>+			return -EFAULT;
>
>return what the op returns: ret

Why would we return here a driver return code, userspace can have this info
from extack. IMHO return values of dpll subsystem shall be not dependent on
what is returned from the driver.

>
>
>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>>+	}
>>+
>>+	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) {
>>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>
>ret = ..
>if (ret)
>	return ret;
>
>Please use this pattern in other ops call code as well.
>

This is the same as above (return code by driver) explanation.

>
>>+			return -EFAULT;
>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>>+	}
>>+
>>+	return 0;
>
>[...]

Thanks,
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] 202+ messages in thread

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

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 4:45 PM
>
>[...]
>
>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index edd3d562beee..0222b19af545 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6289,6 +6289,15 @@ 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
>
>Why "clock"? You don't mention "clock" anywhere else.
>
>[...]
>
>
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..3fc151e16751
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,835 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ *  dpll_core.c - Generic DPLL Management class support.
>
>Why "class" ?
>
>[...]
>
>
>>+static int
>>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>>+{
>>+	enum dpll_pin_freq_supp fs;
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long i;
>>+	u32 freq;
>>+
>>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>>+		if (ref && ref->ops && ref->dpll)
>>+			break;
>>+	}
>>+	if (!ref || !ref->ops || !ref->dpll)
>>+		return -ENODEV;
>>+	if (!ref->ops->frequency_get)
>>+		return -EOPNOTSUPP;
>>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>>+		return -EFAULT;
>>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>>+		return -EMSGSIZE;
>>+	if (!dump_any_freq)
>>+		return 0;
>>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>+			    dpll_pin_freq_value[fs]))
>
>This is odd. As I suggested in the yaml patch, better to treat all
>supported frequencies the same, no matter if it is range or not. The you
>don't need this weird bitfield.
>
>You can have a macro to help driver to assemble array of supported
>frequencies and ranges.
>

I understand suggestion on yaml, but here I am confused.
How do they relate to the supported frequency passed between driver and dpll
subsystem?
This bitfield is not visible to the userspace, and sure probably adding macro
can be useful.

>
>>+				return -EMSGSIZE;
>>+		}
>>+	}
>>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>+				pin->prop.any_freq_min))
>>+			return -EMSGSIZE;
>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>+				pin->prop.any_freq_max))
>>+			return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>
>[...]
>
>
>>+static int
>>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>+			 struct dpll_device *dpll,
>>+			 struct netlink_ext_ack *extack)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	int ret;
>>+
>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>+		return -EMSGSIZE;
>>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>>+		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, extack);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>+	if (!ref)
>
>How exactly this can happen? Looks to me like only in case of a bug.
>WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?

Yes, makes sense.

>
>
>>+		return -EFAULT;
>>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>>+	if (ret && ret != -EOPNOTSUPP)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>>+	if (ret)
>>+		return ret;
>>+	if (pin->rclk_dev_name)
>
>Use && and single if
>

Make sense to me.

>
>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>+				   pin->rclk_dev_name))
>>+			return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>
>[...]
>
>
>>+static int
>>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>>+		  struct netlink_ext_ack *extack)
>>+{
>>+	u32 freq = nla_get_u32(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) {
>>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>>+		if (ret)
>>+			return -EFAULT;
>
>return what the op returns: ret

Why would we return here a driver return code, userspace can have this info
from extack. IMHO return values of dpll subsystem shall be not dependent on
what is returned from the driver.

>
>
>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>>+	}
>>+
>>+	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) {
>>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>
>ret = ..
>if (ret)
>	return ret;
>
>Please use this pattern in other ops call code as well.
>

This is the same as above (return code by driver) explanation.

>
>>+			return -EFAULT;
>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>>+	}
>>+
>>+	return 0;
>
>[...]

Thanks,
Arkadiusz

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-14 10:05   ` Jiri Pirko
@ 2023-03-15  0:10       ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-15  0:10 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 14/03/2023 10:05, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>> 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>> 2 files changed, 200 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>> --- a/drivers/ptp/ptp_ocp.c
>> +++ b/drivers/ptp/ptp_ocp.c
>> @@ -23,6 +23,8 @@
>> #include <linux/mtd/mtd.h>
>> #include <linux/nvmem-consumer.h>
>> #include <linux/crc16.h>
>> +#include <linux/dpll.h>
>> +#include <uapi/linux/dpll.h>
> 
> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
> 

Ah, yeah, you've mentioned it for ice driver last time, but I forgot to 
check it here. Removed.

> 
>>
>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>> 	bool	fixed_dir;
>> 	bool	disabled;
>> 	u8	default_fcn;
>> +	struct dpll_pin *dpll_pin;
>> };
>>
>> struct ocp_attr_group {
>> @@ -353,6 +356,7 @@ struct ptp_ocp {
>> 	struct ptp_ocp_signal	signal[4];
>> 	struct ptp_ocp_sma_connector sma[4];
> 
> It is quite customary to use defines for const numbers like this. Why
> don't you do that in ptp_ocp?

Historical reasons. Jonathan might answer this question.
I will add it to my TODO list for the driver.

>> 	const struct ocp_sma_op *sma_op;
>> +	struct dpll_device *dpll;
>> };
>>
>> #define OCP_REQ_TIMESTAMP	BIT(0)
>> @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> }
>>
>> static int
>> -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> {
>> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> -	enum ptp_ocp_sma_mode mode;
>> -	int val;
>> -
>> -	mode = sma->mode;
>> -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> -	if (val < 0)
>> -		return val;
>>
>> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> 		return -EOPNOTSUPP;
>> @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> 	return val;
>> }
>>
>> +static int
>> +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> +	enum ptp_ocp_sma_mode mode;
>> +	int val;
>> +
>> +	mode = sma->mode;
>> +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> +	if (val < 0)
>> +		return val;
>> +
>> +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> +}
>> +
>> static ssize_t
>> sma1_store(struct device *dev, struct device_attribute *attr,
>> 	   const char *buf, size_t count)
>> @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> 	device_unregister(&bp->dev);
>> }
>>
>> +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < 4; i++) {
>> +		if (bp->sma[i].dpll_pin == pin)
>> +			return i;
> 
> Just pass &bp->sma[i] as a priv to dpll_pin_register().
> and get that pointer directly in pin ops. No need for lookup and use of
> sma_nr at all.

I'm still thinking about the change that you proposed to remove these
_priv() helpers. I have to look into ice code to be sure we won't break
any assumptions in it with moving to additional (void *) parameter.

> 
>> +	}
>> +	return -1;
>> +}
>> +
>> +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> +				    enum dpll_lock_status *status,
>> +				    struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
> 
> No need to cast (void *), remove it.
> 
Fixed everywhere in the code, thanks.


> 
>> +	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,
>> +				    u32 *idx, struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +
>> +	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,
>> +				    u32 *mode, struct netlink_ext_ack *extack)
>> +{
>> +	*mode = DPLL_MODE_AUTOMATIC;
>> +
> 
> No need for empty line, I believe.

Ok.

>> +	return 0;
>> +}
>> +
>> +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> +				    const enum dpll_mode mode,
>> +				    struct netlink_ext_ack *extack)
>> +{
>> +	if (mode == DPLL_MODE_AUTOMATIC)
>> +		return true;
>> +
>> +	return false;
> 
> Just:
> 	return mode == DPLL_MODE_AUTOMATIC;
> 
Done, thanks!

>> +}
>> +
>> +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> +				     const struct dpll_device *dpll,
>> +				     enum dpll_pin_direction *direction,
>> +				     struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	*direction = bp->sma[sma_nr].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,
>> +				     const struct dpll_device *dpll,
>> +				     enum dpll_pin_direction direction,
>> +				     struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	enum ptp_ocp_sma_mode mode;
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	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,
>> +			      const struct dpll_device *dpll,
>> +			      const u32 frequency,
> 
> Why you need const for u32?

No need, true, removed.

> 
>> +			      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	int val = frequency == 10000000 ? 0 : 1;
> 
> This is weird. I believe you should handle unsupported frequencies
> gracefully.
> 

Currently hardware supports fixed frequencies only. That's why I have to
do this kind of "dance". Hopefully we will improve it to support 
variable frequency.

> 
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +
> 
> Avoid double empty lines.
> 

Yep.

> 
>> +	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
>> +}
>> +
>> +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> +			      const struct dpll_device *dpll,
>> +			      u32 *frequency,
>> +			      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	u32 val;
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	val = bp->sma_op->get(bp, sma_nr);
>> +	if (!val)
>> +		*frequency = 1000000;
>> +	else
>> +		*frequency = 0;
> 
> I don't follow. How the frequency could be 0? Does that mean that the
> pin is disabled? If yes, you should rather implement
> .state_on_dpll_get
> .state_on_dpll_set
> 
> and leave the frequency constant.

It's actually a mistake. It should be 1 which means 1Hz, PPS. The value 
returned by sma_op->get is actually the type of the signal where 0 is 
1PPS, 1 is 10Mhz and so on.

> 
> 
>> +	return 0;
>> +}
>> +
>> +static struct dpll_device_ops dpll_ops = {
> 
> const
> 

Will change it together with API part. Otherwise it doesn't compile.

> 
>> +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
> 
> Should be called "source_pin_get" and return (struct dpll_pin *)
> On the driver api level, no reason to pass indexes. Work with objects.
> 

Good point, will improve it.

> 
>> +	.mode_get = ptp_ocp_dpll_mode_get,
>> +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> +};
>> +
>> +static struct dpll_pin_ops dpll_pins_ops = {
> 
> const
> 

Yep.

> 
>> +	.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 dpll_pin_properties prop;
>> 	struct devlink *devlink;
>> +	char sma[4] = "SMA0";
> 
> Won't fit. Just use:
> char *sma = "SMA0"
> to be safe
> 
Got it.

> 
>> 	struct ptp_ocp *bp;
>> -	int err;
>> +	int err, i;
>> +	u64 clkid;
>>
>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> 	if (!devlink) {
>> @@ -4226,8 +4377,44 @@ 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 (!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;
>> +
>> +	prop.description = &sma[0];
>> +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>> +	prop.type = DPLL_PIN_TYPE_EXT;
>> +	prop.any_freq_max = 10000000;
>> +	prop.any_freq_min = 0;
>> +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> +
>> +	for (i = 0; i < 4; i++) {
>> +		sma[3] = 0x31 + i;
> 
> Ugh. Just use the static const array as I suggested in the dpll patch
> reply. Helps you avoid this sort of "magic".
> 
well, yes. but at the same time Arkadiusz raised a good question about
accessing driver's memory from the subsystem code - doesn't look clean.

> 
>> +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>> +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
> 
> How it could be NULL?
> 

Allocation fail?

> 
>> +			bp->sma[i].dpll_pin = NULL;
> 
> This would not be needed if the error path iterates over
> indexes which were successul.
> 

Yeah, I'll make it the same way it's done in ice.


> 
>> +			goto out_dpll;
>> +		}
>> +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
>> +		if (err)
>> +			goto out_dpll;
>> +	}
>> +
>> +	return 0;
>> +out_dpll:
>> +	for (i = 0; i < 4; i++) {
>> +		if (bp->sma[i].dpll_pin)
> 
> You don't do unregister of pin here.
> 
Yeah, actually error path and unload path should be re-implemented.

> 
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +	}
>> +	dpll_device_put(bp->dpll);
>> out:
>> 	ptp_ocp_detach(bp);
>> out_disable:
>> @@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> 	struct devlink *devlink = priv_to_devlink(bp);
>>
>> +	dpll_device_unregister(bp->dpll);
>> +	dpll_device_put(bp->dpll);
>> 	devlink_unregister(devlink);
>> 	ptp_ocp_detach(bp);
>> 	pci_disable_device(pdev);
>> -- 
>> 2.34.1
>>


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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-15  0:10       ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-15  0:10 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 14/03/2023 10:05, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>> 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>> 2 files changed, 200 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>> --- a/drivers/ptp/ptp_ocp.c
>> +++ b/drivers/ptp/ptp_ocp.c
>> @@ -23,6 +23,8 @@
>> #include <linux/mtd/mtd.h>
>> #include <linux/nvmem-consumer.h>
>> #include <linux/crc16.h>
>> +#include <linux/dpll.h>
>> +#include <uapi/linux/dpll.h>
> 
> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
> 

Ah, yeah, you've mentioned it for ice driver last time, but I forgot to 
check it here. Removed.

> 
>>
>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>> 	bool	fixed_dir;
>> 	bool	disabled;
>> 	u8	default_fcn;
>> +	struct dpll_pin *dpll_pin;
>> };
>>
>> struct ocp_attr_group {
>> @@ -353,6 +356,7 @@ struct ptp_ocp {
>> 	struct ptp_ocp_signal	signal[4];
>> 	struct ptp_ocp_sma_connector sma[4];
> 
> It is quite customary to use defines for const numbers like this. Why
> don't you do that in ptp_ocp?

Historical reasons. Jonathan might answer this question.
I will add it to my TODO list for the driver.

>> 	const struct ocp_sma_op *sma_op;
>> +	struct dpll_device *dpll;
>> };
>>
>> #define OCP_REQ_TIMESTAMP	BIT(0)
>> @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> }
>>
>> static int
>> -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> {
>> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> -	enum ptp_ocp_sma_mode mode;
>> -	int val;
>> -
>> -	mode = sma->mode;
>> -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> -	if (val < 0)
>> -		return val;
>>
>> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> 		return -EOPNOTSUPP;
>> @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> 	return val;
>> }
>>
>> +static int
>> +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> +	enum ptp_ocp_sma_mode mode;
>> +	int val;
>> +
>> +	mode = sma->mode;
>> +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> +	if (val < 0)
>> +		return val;
>> +
>> +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> +}
>> +
>> static ssize_t
>> sma1_store(struct device *dev, struct device_attribute *attr,
>> 	   const char *buf, size_t count)
>> @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> 	device_unregister(&bp->dev);
>> }
>>
>> +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < 4; i++) {
>> +		if (bp->sma[i].dpll_pin == pin)
>> +			return i;
> 
> Just pass &bp->sma[i] as a priv to dpll_pin_register().
> and get that pointer directly in pin ops. No need for lookup and use of
> sma_nr at all.

I'm still thinking about the change that you proposed to remove these
_priv() helpers. I have to look into ice code to be sure we won't break
any assumptions in it with moving to additional (void *) parameter.

> 
>> +	}
>> +	return -1;
>> +}
>> +
>> +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> +				    enum dpll_lock_status *status,
>> +				    struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
> 
> No need to cast (void *), remove it.
> 
Fixed everywhere in the code, thanks.


> 
>> +	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,
>> +				    u32 *idx, struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +
>> +	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,
>> +				    u32 *mode, struct netlink_ext_ack *extack)
>> +{
>> +	*mode = DPLL_MODE_AUTOMATIC;
>> +
> 
> No need for empty line, I believe.

Ok.

>> +	return 0;
>> +}
>> +
>> +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> +				    const enum dpll_mode mode,
>> +				    struct netlink_ext_ack *extack)
>> +{
>> +	if (mode == DPLL_MODE_AUTOMATIC)
>> +		return true;
>> +
>> +	return false;
> 
> Just:
> 	return mode == DPLL_MODE_AUTOMATIC;
> 
Done, thanks!

>> +}
>> +
>> +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> +				     const struct dpll_device *dpll,
>> +				     enum dpll_pin_direction *direction,
>> +				     struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	*direction = bp->sma[sma_nr].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,
>> +				     const struct dpll_device *dpll,
>> +				     enum dpll_pin_direction direction,
>> +				     struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	enum ptp_ocp_sma_mode mode;
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	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,
>> +			      const struct dpll_device *dpll,
>> +			      const u32 frequency,
> 
> Why you need const for u32?

No need, true, removed.

> 
>> +			      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	int val = frequency == 10000000 ? 0 : 1;
> 
> This is weird. I believe you should handle unsupported frequencies
> gracefully.
> 

Currently hardware supports fixed frequencies only. That's why I have to
do this kind of "dance". Hopefully we will improve it to support 
variable frequency.

> 
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +
> 
> Avoid double empty lines.
> 

Yep.

> 
>> +	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
>> +}
>> +
>> +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> +			      const struct dpll_device *dpll,
>> +			      u32 *frequency,
>> +			      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> +	u32 val;
>> +
>> +	if (sma_nr < 0)
>> +		return -EINVAL;
>> +
>> +	val = bp->sma_op->get(bp, sma_nr);
>> +	if (!val)
>> +		*frequency = 1000000;
>> +	else
>> +		*frequency = 0;
> 
> I don't follow. How the frequency could be 0? Does that mean that the
> pin is disabled? If yes, you should rather implement
> .state_on_dpll_get
> .state_on_dpll_set
> 
> and leave the frequency constant.

It's actually a mistake. It should be 1 which means 1Hz, PPS. The value 
returned by sma_op->get is actually the type of the signal where 0 is 
1PPS, 1 is 10Mhz and so on.

> 
> 
>> +	return 0;
>> +}
>> +
>> +static struct dpll_device_ops dpll_ops = {
> 
> const
> 

Will change it together with API part. Otherwise it doesn't compile.

> 
>> +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
> 
> Should be called "source_pin_get" and return (struct dpll_pin *)
> On the driver api level, no reason to pass indexes. Work with objects.
> 

Good point, will improve it.

> 
>> +	.mode_get = ptp_ocp_dpll_mode_get,
>> +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> +};
>> +
>> +static struct dpll_pin_ops dpll_pins_ops = {
> 
> const
> 

Yep.

> 
>> +	.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 dpll_pin_properties prop;
>> 	struct devlink *devlink;
>> +	char sma[4] = "SMA0";
> 
> Won't fit. Just use:
> char *sma = "SMA0"
> to be safe
> 
Got it.

> 
>> 	struct ptp_ocp *bp;
>> -	int err;
>> +	int err, i;
>> +	u64 clkid;
>>
>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> 	if (!devlink) {
>> @@ -4226,8 +4377,44 @@ 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 (!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;
>> +
>> +	prop.description = &sma[0];
>> +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>> +	prop.type = DPLL_PIN_TYPE_EXT;
>> +	prop.any_freq_max = 10000000;
>> +	prop.any_freq_min = 0;
>> +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> +
>> +	for (i = 0; i < 4; i++) {
>> +		sma[3] = 0x31 + i;
> 
> Ugh. Just use the static const array as I suggested in the dpll patch
> reply. Helps you avoid this sort of "magic".
> 
well, yes. but at the same time Arkadiusz raised a good question about
accessing driver's memory from the subsystem code - doesn't look clean.

> 
>> +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>> +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
> 
> How it could be NULL?
> 

Allocation fail?

> 
>> +			bp->sma[i].dpll_pin = NULL;
> 
> This would not be needed if the error path iterates over
> indexes which were successul.
> 

Yeah, I'll make it the same way it's done in ice.


> 
>> +			goto out_dpll;
>> +		}
>> +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
>> +		if (err)
>> +			goto out_dpll;
>> +	}
>> +
>> +	return 0;
>> +out_dpll:
>> +	for (i = 0; i < 4; i++) {
>> +		if (bp->sma[i].dpll_pin)
> 
> You don't do unregister of pin here.
> 
Yeah, actually error path and unload path should be re-implemented.

> 
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +	}
>> +	dpll_device_put(bp->dpll);
>> out:
>> 	ptp_ocp_detach(bp);
>> out_disable:
>> @@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> 	struct devlink *devlink = priv_to_devlink(bp);
>>
>> +	dpll_device_unregister(bp->dpll);
>> +	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] 202+ messages in thread

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

Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 10:22 AM
>>
>>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>>
>>[...]
>>
>>
>>>> > diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>>> > new file mode 100644
>>>> > index 000000000000..d3926f2a733d
>>>> > --- /dev/null
>>>> > +++ b/drivers/dpll/Makefile
>>>> > @@ -0,0 +1,10 @@
>>>> > +# SPDX-License-Identifier: GPL-2.0
>>>> > +#
>>>> > +# Makefile for DPLL drivers.
>>>> > +#
>>>> > +
>>>> > +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>>
>>>> What's "sys" and why is it here?
>>>
>>>It's an object file for the subsystem. Could be useful if we will have
>>>drivers
>>>for DPLL-only devices.
>>
>>Yeah, but why "sys"? I don't get what "sys" means here.
>>Can't this be just "dpll.o"?
>>
>>
>>>
>>>> > +dpll_sys-y                  += dpll_core.o
>>>> > +dpll_sys-y                  += dpll_netlink.o
>>>> > +dpll_sys-y                  += dpll_nl.o
>>>> > +
>>
>>[...]
>>
>>
>>>> > +struct dpll_device *
>>>> > +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>>> > *module)
>>>> > +{
>>>> > +	struct dpll_device *dpll, *ret = NULL;
>>>> > +	unsigned long index;
>>>> > +
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>> > +		if (dpll->clock_id == clock_id &&
>>>> > +		    dpll->dev_driver_id == dev_driver_id &&
>>>>
>>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>>> identification, isn't that enough for you.
>>>
>>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>>driver
>>>implementation there are 2 different DPLLs - to recover from PPS input and
>>>to
>>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>>is the same for both of them. But Arkadiusz can tell more about it.
>>
>>Okay, I see. Clock_id is the same. Could we have index for pin, could
>>this be index too:
>>
>>dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>>dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>>	     const struct dpll_pin_properties *prop);
>>
>>This way it is consistent, driver provides custom index for both dpll
>>device and dpll pin.
>>
>>Makes sense?
>>
>
>IMHO, Yes this better shows the intentions.

Ok.


>
>>
>>>>
>>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>>
>>>Simply id doesn't tell anything either. dpll_dev_id?
>>
>>Yeah, see above.
>>
>>
>>>
>>>> > +		    dpll->module == module) {
>>>> > +			ret = dpll;
>>>> > +			refcount_inc(&ret->refcount);
>>>> > +			break;
>>>> > +		}
>>>> > +	}
>>>> > +	if (!ret)
>>>> > +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>> > +	mutex_unlock(&dpll_device_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;
>>>>
>>>> Remove this check. The driver should not call this with NULL.
>>>
>>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>
>>IDK, maybe for historical reasons. My point is, id driver is callin
>>this with NULL, there is something odd in the driver flow. Lets not
>>allow that for new code.
>>
>>
>>>
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>> > +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>>
>>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>>
>>>Good point!
>>>
>>>> > +		xa_destroy(&dpll->pin_refs);
>>>> > +		xa_erase(&dpll_device_xa, dpll->id);
>>>> > +		kfree(dpll);
>>>> > +	}
>>>> > +	mutex_unlock(&dpll_device_xa_lock);
>>>> > +}
>>>> > +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>> > +
>>>> > +/**
>>>> > + * 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,
>>>> > +			 struct dpll_device_ops *ops, void *priv,
>>>> > +			 struct device *owner)
>>>> > +{
>>>> > +	if (WARN_ON(!ops || !owner))
>>>> > +		return -EINVAL;
>>>> > +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>> > +		return -EINVAL;
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>> > +		mutex_unlock(&dpll_device_xa_lock);
>>>> > +		return -EEXIST;
>>>> > +	}
>>>> > +	dpll->dev.bus = owner->bus;
>>>> > +	dpll->parent = owner;
>>>> > +	dpll->type = type;
>>>> > +	dpll->ops = ops;
>>>> > +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>> > +		     dpll->dev_driver_id);
>>>>
>>>> This is really odd. As a result, the user would see something like:
>>>> pci/0000:01:00.0_1
>>>> pci/0000:01:00.0_2
>>>>
>>>> I have to say it is confusing. In devlink, is bus/name and the user
>>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>>> there. Also, "_" might have some meaning on some bus. Should not
>>>> concatename dev_name() with anything.
>>>>
>>>> Thinking about this some more, the module/clock_id tuple should be
>>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>>> as the user handle, can't it?
>>>> Example:
>>>> ice/c92d02a7129f4747
>>>> mlx5/90265d8bf6e6df56
>>>>
>>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>>> enough), you can put it here as well:
>>>> ice/c92d02a7129f4747/1
>>>> ice/c92d02a7129f4747/2
>>>>
>>>
>>>Looks good, will change it
>>
>>Great.
>>
>>
>>>
>>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>>> share instance of DPLL equally, there is no "one clock master". >
>>
>>[...]
>>
>>
>>>> > +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>> > +	if (!pin->prop.description) {
>>>> > +		ret = -ENOMEM;
>>>> > +		goto release;
>>>> > +	}
>>>> > +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>> > +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>> > +		ret = -EINVAL;
>>>> > +		goto release;
>>>> > +	}
>>>> > +	pin->prop.type = prop->type;
>>>> > +	pin->prop.capabilities = prop->capabilities;
>>>> > +	pin->prop.freq_supported = prop->freq_supported;
>>>> > +	pin->prop.any_freq_min = prop->any_freq_min;
>>>> > +	pin->prop.any_freq_max = prop->any_freq_max;
>>>>
>>>> Make sure that the driver maintains prop (static const) and just save
>>>> the pointer. Prop does not need to be something driver needs to change.
>>>>
>>>
>>>What's the difference? For ptp_ocp, we have the same configuration for all
>>>ext pins and the allocator only changes the name of the pin. Properties of
>>>the DPLL pins are stored within the pin object, not the driver, in this
>>case.
>>>Not sure if the pointer way is much better...
>>
>>For things like this it is common to have static const array in the
>>driver, like:
>>
>>static const struct dpll_pin_properties dpll_pin_props[] = {
>>	{
>>		.description = "SMA0",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA1",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA2",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA3",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>};
>>
>>Here you have very nice list of pins, the reader knows right away what
>>is happening.
>>
>>Thinking about "description" name, I think would be more appropriate to
>>name this "label" as it represents user-facing label on the connector,
>>isn't it? Does not describe anything.
>>
>
>"label" seems good.

Ok.


>
>>
>>>
>>>>
>>
>>[...]
>>
>>
>>>
>>>>
>>>> > +	     const struct dpll_pin_properties *prop)
>>>> > +{
>>>> > +	struct dpll_pin *pos, *ret = NULL;
>>>> > +	unsigned long index;
>>>> > +
>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>> > +		if (pos->clock_id == clock_id &&
>>>> > +		    pos->dev_driver_id == device_drv_id &&
>>>> > +		    pos->module == module) {
>>>>
>>>> Compare prop as well.
>>>>
>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>> should. And therefore it could be easily put inside.
>>>>
>>>
>>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>>thoughts?
>>
>>IDK, no strong opinion on this. I just thought it may help to identify
>>the pin and avoid potential driver bugs. (Like registering 2 pins with
>>the same properties).
>>
>
>It would make most sense if pin_index would be a part of *prop.

Hmm. I see one example where it would not be suitable:
NIC with N physical ports. You, as a driver, register one pin per
physical port. You have one static const prop and pass the pointer to if
for every registration call of N pins, each time with different
pin_index.

So from what I see, the prop describes a flavour of pin in the driver.
In the exaple above, it is still the same SyncE pin, with the same ops.
Only multiple instances of that distinguished by pin_index and priv.

Makes sense?


>
>>[...]
>>
>>
>>>> > +/**
>>>> > + * 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,
>>>> > +		  struct dpll_pin_ops *ops, void *priv,
>>>> > +		  struct device *rclk_device)
>>>>
>>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>>
>>>>
>>>> > +{
>>>> > +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>>> >NULL;
>>>>
>>>> If you need to store something here, store the pointer to the device
>>>> directly. But this rclk_device seems odd to me.
>>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>>> is incomplete. What should it server for?
>>>>
>>>
>>>Well, these questions go to Arkadiusz...
>>
>
>
>[ copy paste my answer from previous response ]

Does not help, see below.


>If pin is able to recover signal from some device this shall convey that
>device struct pointer.

But one device (struct device instance) could easily have multiple
netdev instances attached and therefore mutiple sources of recovered
signal.

mlxsw driver is one of the examples of this 1:N mapping between device
and netdev.


>Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>attribute.
>Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>part. But isn't it better to have the name ready to use there?
>
>It might be incomplete only if one device would have some kind of access to
>a different bus? I don't think it is valid use case.

Very easily, auxiliary_bus. That is actually the case already for mlx5.


>
>Basically the driver will refer only to the devices handled by that driver,
>which means if dpll is on some bus, also all the pins are there, didn't notice
>the need to have bus here as well.

What is the motivation exactly? Is it only SyncE? In case it is, the
link to the pin should be added from the other side, carried over
RTNetlink msg of a netdev associated with pin. I will add a patch for
this.

Do you need to expose any other recovered clock device source?


>
>>Okay.
>>
>>[...]
>>
>>
>>>> > + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>>> > + * @dpll: dpll device pointer
>>>> > + * @idx: index of pin
>>>> > + *
>>>> > + * Find a reference to a pin registered with given dpll and return
>>>> > its pointer.
>>>> > + *
>>>> > + * Return:
>>>> > + * * valid pointer if pin was found
>>>> > + * * NULL if not found
>>>> > + */
>>>> > +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>> > +{
>>>> > +	struct dpll_pin_ref *pos;
>>>> > +	unsigned long i;
>>>> > +
>>>> > +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>> > +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>>
>>>> How exactly pos->pin could be NULL?
>>>>
>>>> Also, you are degrading the xarray to a mere list here with lookup like
>>>> this. Why can't you use the pin index coming from driver and
>>>> insert/lookup based on this index?
>>>>
>>>Good point. We just have to be sure, that drivers provide 0-based indexes for
>>>their pins. I'll re-think it.
>>
>>No, driver can provide indexing which is completely up to his decision.
>>You should use xa_insert() to insert the entry at specific index. See
>>devl_port_register for inspiration where it is done exactly like this.
>>
>>And this should be done in exactly the same way for both pin and device.
>>
>
>Yes, I agree seems doable and better then it is now.
>
>[...]
>
>>>> > +static int
>>>> > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>>> >*dpll)
>>>> > +{
>>>> > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>>
>>>> Why exactly do we need this dua--handle scheme? Why do you need
>>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>>> It's just confusing.
>>>>
>>>To be able to work with DPLL per integer after iterator on the list deducts
>>>which DPLL device is needed. It can reduce the amount of memory copies and
>>>simplify comparisons. Not sure why it's confusing.
>>
>>Wait, I don't get it. Could you please explain a bit more?
>>
>>My point is, there should be not such ID exposed over netlink
>>You don't need to expose it to userspace. The user has well defined
>>handle as you agreed with above. For example:
>>
>>ice/c92d02a7129f4747/1
>>ice/c92d02a7129f4747/2
>>
>>This is shown in dpll device GET/DUMP outputs.
>>Also user passes it during SET operation:
>>$ dplltool set ice/c92d02a7129f4747/1 mode auto
>>
>>Isn't that enough stable and nice?
>>
>
>I agree with Vadim, this is rather to be used by a daemon tools, which
>would get the index once, then could use it as long as device is there.

So basically you say, you can have 2 approaches in app:
1)
id = dpll_device_get_id("ice/c92d02a7129f4747/1")
dpll_device_set(id, something);
dpll_device_set(id, something);
dpll_device_set(id, something);
2):
dpll_device_set("ice/c92d02a7129f4747/1, something);
dpll_device_set("ice/c92d02a7129f4747/1, something);
dpll_device_set("ice/c92d02a7129f4747/1, something);

What is exactly benefit of the first one? Why to have 2 handles? Devlink
is a nice example of 2) approach, no problem there.

Perhaps I'm missing something, but looks like you want the id for no
good reason and this dual-handle scheme just makes things more
complicated with 0 added value.


>
>>
>>>
>>>>
>>>> > +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>> > +			 struct netlink_ext_ack *extack)
>>>> > +{
>>>> > +	struct dpll_pin_ref *ref = NULL;
>>>>
>>>> Why this needs to be initialized?
>>>>
>>>No need, fixed.
>>>
>>>
>>>>
>>>> > +	enum dpll_pin_state state;
>>>> > +	struct nlattr *nest;
>>>> > +	unsigned long index;
>>>> > +	int ret;
>>>> > +
>>>> > +	xa_for_each(&pin->parent_refs, index, ref) {
>>>> > +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>> > +			return -EFAULT;
>>>> > +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>> > +				ref->pin->dev_driver_id)) {
>>>> > +			ret = -EMSGSIZE;
>>>> > +			goto nest_cancel;
>>>> > +		}
>>>> > +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>> > +			ret = -EMSGSIZE;
>>>> > +			goto nest_cancel;
>>>> > +		}
>>>> > +		nla_nest_end(msg, nest);
>>>> > +	}
>>>>
>>>> How is this function different to dpll_msg_add_pin_parents()?
>>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>>> hard to follow for me :/
>>>>
>>>> Did you get lost here as well? If yes, this needs some serious think
>>>> through :)
>>>>
>>>
>>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>>relationship between these things?
>>
>
>[ copy paste my answer from previous response ]
>No, it is just leftover I didn't catch, we can leave one function and use it in
>both cases. Sorry about that, great catch!

Hmm, ok. I'm still lost a bit with all the referencing and cross
referencing, I wonder if it could be made a bit simpler and/or easier to
read and follow.


>
>[...]
>
>>>> > +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>>
>>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>>> code.
>>>>
>>>
>>>I believe it's INDEX which is provided by the driver. I'll think about
>>>renaming,
>>>but suggestions are welcome.
>>
>>Let's use "index" and "INDEX" internalla and in Netlink attr names as
>>well then.
>>
>
>For me makes sense to have a common name instead of origin-based one.

What do you mean by this?


>
>>[...]
>>
>>
>>>
>>>>
>>>> > +	int rem, ret = -EINVAL;
>>>> > +	struct nlattr *a;
>>>> > +
>>>> > +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>> > +			  genlmsg_len(info->genlhdr), rem) {
>>>>
>>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>>
>>>
>>>I had some unknown crashes when I was using such access. I might have lost
>>>some checks, will try it again.
>>
>>Odd, yet definitelly debuggable though :)
>>
>>[...]
>>
>>
>>>> > +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>> > +{
>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>>
>>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>>> taking order is opposite.
>>>>
>>>
>>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>>how to
>>>solve it here. Any thoughts?
>>
>>Well, here you can just call dpll_pre_dumpit() before
>>mutex_lock(&dpll_pin_xa_lock)
>>to take the locks in the same order.
>>
>
>This double lock doesn't really improve anything.
>Any objections on having single mutex/lock for access the dpll subsystem?

Yeah, we had it in devlink and then we spent a lot of a time to get rid
of it. Now we have per-instance locking. Here, it would be
per-dpll-device locking.

No strong opinion, perhaps on case of dpll one master lock is enough.
Perhaps Jakub would have some opinion on this.


>
>>
>>>
>>>>
>>>> > +
>>>> > +	return dpll_pre_dumpit(cb);
>>>> > +}
>>>> > +
>>>> > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>> > +{
>>>> > +	mutex_unlock(&dpll_pin_xa_lock);
>>>> > +
>>>> > +	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-
>>>> > dev_driver_id))
>>>> > +		return -EMSGSIZE;
>>>>
>>>> I don't really understand why you are trying figure something new and
>>>> interesting with the change notifications. This object mix and random
>>>> attrs fillup is something very wrong and makes userspace completely
>>>> fuzzy about what it is getting. And yet it is so simple:
>>>> You have 2 objects, dpll and pin, please just have:
>>>> dpll_notify()
>>>> dpll_pin_notify()
>>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>>> No need for any smartness. Have this dumb and simple.
>>>>
>>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>>
>>>But with full object-snapshot user space app will lose the information about
>>>what exactly has changed. The reason to have this event is to provide the
>>>attributes which have changed. Otherwise, the app should have full snapshot
>>>and
>>>compare all attributes to figure out changes and that's might not be great
>>>idea.
>>
>>Wait, are you saying that the app is stateless? Could you provide
>>example use cases?
>>
>>From what I see, the app managing dpll knows the state of the device and
>>pins, it monitors for the changes and saves new state with appropriate
>>reaction (might be some action or maybe just log entry).
>>
>
>It depends on the use case, right? App developer having those information knows
>what has changed, thus can react in a way it thinks is most suitable.
>IMHO from user perspective it is good to have a notification which actually
>shows it's reason, so proper flow could be assigned to handle the reaction.

Again, could you provide me specific example in which this is needed?
I may be missing something, but I don't see how it can bring
and benefit. It just makes the live of the app harder because it has to
treat the get and notify messages differently.

It is quite common for app to:
init:
1) get object state
2) store it
3) apply configuration
runtime:
1) listen to object state change
2) store it
3) apply configuration

Same code for both.




>
>>
>>>
>>>>
>>>> > +
>>>> > +	switch (attr) {
>>>> > +	case DPLL_A_MODE:
>>>> > +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>>> > +		break;
>>>> > +	case DPLL_A_SOURCE_PIN_IDX:
>>>> > +		ret = dpll_msg_add_source_pin_idx(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:
>>>> > +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>>> > +		break;
>>>> > +	case DPLL_A_PIN_PRIO:
>>>> > +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>> > +		if (!ref)
>>>> > +			return -EFAULT;
>>>> > +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>>> > +		break;
>>>> > +	case DPLL_A_PIN_STATE:
>>>> > +		if (parent) {
>>>> > +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>>> > +			if (!ref)
>>>> > +				return -EFAULT;
>>>> > +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>>> > +				return -EOPNOTSUPP;
>>>> > +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>>> > +							 NULL);
>>>> > +			if (ret)
>>>> > +				return ret;
>>>> > +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>>> > +					parent->dev_driver_id))
>>>> > +				return -EMSGSIZE;
>>>> > +		} else {
>>>> > +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>> > +			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);
>>>>
>>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>>> I have to be missing something.
>>>
>>>Changes might come from other places, but will affect the DPLL device and we
>>>have to notify users in this case.
>>
>>I'm not sure I follow. There are 2 scenarios for change:
>>1) user originated - user issues set of something
>>2) driver originated - something changes in HW, driver propagates that
>>
>>With what I suggest, both scenarios work of course. My point is, user
>>knows very well the objects: device and pin, he knows the format or
>>messages that are related to GET/DUMP/SET operations of both. The
>>notification should have the same format, that is all I say.
>>
>>Btw, you can see devlink code for example how the notifications like
>>this are implemented and work.
>>
>
>Devlink packs all the info into a netlink message and notifies with it, isn't
>it that it has all the info "buffered" in its structures?

Not really. Ops are there as well.


>A dpll subsystem keeps only some of the info in its internal structures, but
>for most of it it has to question the driver. It would do it multiple times
>i.e. if there would be a change on a pin connected to multiple dpll devices.
>
>With current approach the notifications are pretty light to the subsystem as
>well as for the registered clients, and as you suggested with having info of
>what actually changed the userspace could implement a stateless daemon right
>away.

Okay, examples? I really can't see how this stateless deamon could be
implemented, but perhaps I lack better imagination :/


>
>Thank you,
>Arkadiusz

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

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

Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 10:22 AM
>>
>>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>>
>>[...]
>>
>>
>>>> > diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>>> > new file mode 100644
>>>> > index 000000000000..d3926f2a733d
>>>> > --- /dev/null
>>>> > +++ b/drivers/dpll/Makefile
>>>> > @@ -0,0 +1,10 @@
>>>> > +# SPDX-License-Identifier: GPL-2.0
>>>> > +#
>>>> > +# Makefile for DPLL drivers.
>>>> > +#
>>>> > +
>>>> > +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>>
>>>> What's "sys" and why is it here?
>>>
>>>It's an object file for the subsystem. Could be useful if we will have
>>>drivers
>>>for DPLL-only devices.
>>
>>Yeah, but why "sys"? I don't get what "sys" means here.
>>Can't this be just "dpll.o"?
>>
>>
>>>
>>>> > +dpll_sys-y                  += dpll_core.o
>>>> > +dpll_sys-y                  += dpll_netlink.o
>>>> > +dpll_sys-y                  += dpll_nl.o
>>>> > +
>>
>>[...]
>>
>>
>>>> > +struct dpll_device *
>>>> > +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>>> > *module)
>>>> > +{
>>>> > +	struct dpll_device *dpll, *ret = NULL;
>>>> > +	unsigned long index;
>>>> > +
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>> > +		if (dpll->clock_id == clock_id &&
>>>> > +		    dpll->dev_driver_id == dev_driver_id &&
>>>>
>>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>>> identification, isn't that enough for you.
>>>
>>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>>driver
>>>implementation there are 2 different DPLLs - to recover from PPS input and
>>>to
>>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>>is the same for both of them. But Arkadiusz can tell more about it.
>>
>>Okay, I see. Clock_id is the same. Could we have index for pin, could
>>this be index too:
>>
>>dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>>dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>>	     const struct dpll_pin_properties *prop);
>>
>>This way it is consistent, driver provides custom index for both dpll
>>device and dpll pin.
>>
>>Makes sense?
>>
>
>IMHO, Yes this better shows the intentions.

Ok.


>
>>
>>>>
>>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>>
>>>Simply id doesn't tell anything either. dpll_dev_id?
>>
>>Yeah, see above.
>>
>>
>>>
>>>> > +		    dpll->module == module) {
>>>> > +			ret = dpll;
>>>> > +			refcount_inc(&ret->refcount);
>>>> > +			break;
>>>> > +		}
>>>> > +	}
>>>> > +	if (!ret)
>>>> > +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>> > +	mutex_unlock(&dpll_device_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;
>>>>
>>>> Remove this check. The driver should not call this with NULL.
>>>
>>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>
>>IDK, maybe for historical reasons. My point is, id driver is callin
>>this with NULL, there is something odd in the driver flow. Lets not
>>allow that for new code.
>>
>>
>>>
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>> > +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>>
>>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>>
>>>Good point!
>>>
>>>> > +		xa_destroy(&dpll->pin_refs);
>>>> > +		xa_erase(&dpll_device_xa, dpll->id);
>>>> > +		kfree(dpll);
>>>> > +	}
>>>> > +	mutex_unlock(&dpll_device_xa_lock);
>>>> > +}
>>>> > +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>> > +
>>>> > +/**
>>>> > + * 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,
>>>> > +			 struct dpll_device_ops *ops, void *priv,
>>>> > +			 struct device *owner)
>>>> > +{
>>>> > +	if (WARN_ON(!ops || !owner))
>>>> > +		return -EINVAL;
>>>> > +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>> > +		return -EINVAL;
>>>> > +	mutex_lock(&dpll_device_xa_lock);
>>>> > +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>> > +		mutex_unlock(&dpll_device_xa_lock);
>>>> > +		return -EEXIST;
>>>> > +	}
>>>> > +	dpll->dev.bus = owner->bus;
>>>> > +	dpll->parent = owner;
>>>> > +	dpll->type = type;
>>>> > +	dpll->ops = ops;
>>>> > +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>> > +		     dpll->dev_driver_id);
>>>>
>>>> This is really odd. As a result, the user would see something like:
>>>> pci/0000:01:00.0_1
>>>> pci/0000:01:00.0_2
>>>>
>>>> I have to say it is confusing. In devlink, is bus/name and the user
>>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>>> there. Also, "_" might have some meaning on some bus. Should not
>>>> concatename dev_name() with anything.
>>>>
>>>> Thinking about this some more, the module/clock_id tuple should be
>>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>>> as the user handle, can't it?
>>>> Example:
>>>> ice/c92d02a7129f4747
>>>> mlx5/90265d8bf6e6df56
>>>>
>>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>>> enough), you can put it here as well:
>>>> ice/c92d02a7129f4747/1
>>>> ice/c92d02a7129f4747/2
>>>>
>>>
>>>Looks good, will change it
>>
>>Great.
>>
>>
>>>
>>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>>> share instance of DPLL equally, there is no "one clock master". >
>>
>>[...]
>>
>>
>>>> > +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>> > +	if (!pin->prop.description) {
>>>> > +		ret = -ENOMEM;
>>>> > +		goto release;
>>>> > +	}
>>>> > +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>> > +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>> > +		ret = -EINVAL;
>>>> > +		goto release;
>>>> > +	}
>>>> > +	pin->prop.type = prop->type;
>>>> > +	pin->prop.capabilities = prop->capabilities;
>>>> > +	pin->prop.freq_supported = prop->freq_supported;
>>>> > +	pin->prop.any_freq_min = prop->any_freq_min;
>>>> > +	pin->prop.any_freq_max = prop->any_freq_max;
>>>>
>>>> Make sure that the driver maintains prop (static const) and just save
>>>> the pointer. Prop does not need to be something driver needs to change.
>>>>
>>>
>>>What's the difference? For ptp_ocp, we have the same configuration for all
>>>ext pins and the allocator only changes the name of the pin. Properties of
>>>the DPLL pins are stored within the pin object, not the driver, in this
>>case.
>>>Not sure if the pointer way is much better...
>>
>>For things like this it is common to have static const array in the
>>driver, like:
>>
>>static const struct dpll_pin_properties dpll_pin_props[] = {
>>	{
>>		.description = "SMA0",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA1",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA2",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>	{
>>		.description = "SMA3",
>>		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>		.type = DPLL_PIN_TYPE_EXT,
>>		.any_freq_max = 10000000,
>>		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>	},
>>};
>>
>>Here you have very nice list of pins, the reader knows right away what
>>is happening.
>>
>>Thinking about "description" name, I think would be more appropriate to
>>name this "label" as it represents user-facing label on the connector,
>>isn't it? Does not describe anything.
>>
>
>"label" seems good.

Ok.


>
>>
>>>
>>>>
>>
>>[...]
>>
>>
>>>
>>>>
>>>> > +	     const struct dpll_pin_properties *prop)
>>>> > +{
>>>> > +	struct dpll_pin *pos, *ret = NULL;
>>>> > +	unsigned long index;
>>>> > +
>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>> > +		if (pos->clock_id == clock_id &&
>>>> > +		    pos->dev_driver_id == device_drv_id &&
>>>> > +		    pos->module == module) {
>>>>
>>>> Compare prop as well.
>>>>
>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>> should. And therefore it could be easily put inside.
>>>>
>>>
>>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>>thoughts?
>>
>>IDK, no strong opinion on this. I just thought it may help to identify
>>the pin and avoid potential driver bugs. (Like registering 2 pins with
>>the same properties).
>>
>
>It would make most sense if pin_index would be a part of *prop.

Hmm. I see one example where it would not be suitable:
NIC with N physical ports. You, as a driver, register one pin per
physical port. You have one static const prop and pass the pointer to if
for every registration call of N pins, each time with different
pin_index.

So from what I see, the prop describes a flavour of pin in the driver.
In the exaple above, it is still the same SyncE pin, with the same ops.
Only multiple instances of that distinguished by pin_index and priv.

Makes sense?


>
>>[...]
>>
>>
>>>> > +/**
>>>> > + * 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,
>>>> > +		  struct dpll_pin_ops *ops, void *priv,
>>>> > +		  struct device *rclk_device)
>>>>
>>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>>
>>>>
>>>> > +{
>>>> > +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>>> >NULL;
>>>>
>>>> If you need to store something here, store the pointer to the device
>>>> directly. But this rclk_device seems odd to me.
>>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>>> is incomplete. What should it server for?
>>>>
>>>
>>>Well, these questions go to Arkadiusz...
>>
>
>
>[ copy paste my answer from previous response ]

Does not help, see below.


>If pin is able to recover signal from some device this shall convey that
>device struct pointer.

But one device (struct device instance) could easily have multiple
netdev instances attached and therefore mutiple sources of recovered
signal.

mlxsw driver is one of the examples of this 1:N mapping between device
and netdev.


>Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>attribute.
>Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>part. But isn't it better to have the name ready to use there?
>
>It might be incomplete only if one device would have some kind of access to
>a different bus? I don't think it is valid use case.

Very easily, auxiliary_bus. That is actually the case already for mlx5.


>
>Basically the driver will refer only to the devices handled by that driver,
>which means if dpll is on some bus, also all the pins are there, didn't notice
>the need to have bus here as well.

What is the motivation exactly? Is it only SyncE? In case it is, the
link to the pin should be added from the other side, carried over
RTNetlink msg of a netdev associated with pin. I will add a patch for
this.

Do you need to expose any other recovered clock device source?


>
>>Okay.
>>
>>[...]
>>
>>
>>>> > + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>>> > + * @dpll: dpll device pointer
>>>> > + * @idx: index of pin
>>>> > + *
>>>> > + * Find a reference to a pin registered with given dpll and return
>>>> > its pointer.
>>>> > + *
>>>> > + * Return:
>>>> > + * * valid pointer if pin was found
>>>> > + * * NULL if not found
>>>> > + */
>>>> > +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>> > +{
>>>> > +	struct dpll_pin_ref *pos;
>>>> > +	unsigned long i;
>>>> > +
>>>> > +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>> > +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>>
>>>> How exactly pos->pin could be NULL?
>>>>
>>>> Also, you are degrading the xarray to a mere list here with lookup like
>>>> this. Why can't you use the pin index coming from driver and
>>>> insert/lookup based on this index?
>>>>
>>>Good point. We just have to be sure, that drivers provide 0-based indexes for
>>>their pins. I'll re-think it.
>>
>>No, driver can provide indexing which is completely up to his decision.
>>You should use xa_insert() to insert the entry at specific index. See
>>devl_port_register for inspiration where it is done exactly like this.
>>
>>And this should be done in exactly the same way for both pin and device.
>>
>
>Yes, I agree seems doable and better then it is now.
>
>[...]
>
>>>> > +static int
>>>> > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>>> >*dpll)
>>>> > +{
>>>> > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>>
>>>> Why exactly do we need this dua--handle scheme? Why do you need
>>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>>> It's just confusing.
>>>>
>>>To be able to work with DPLL per integer after iterator on the list deducts
>>>which DPLL device is needed. It can reduce the amount of memory copies and
>>>simplify comparisons. Not sure why it's confusing.
>>
>>Wait, I don't get it. Could you please explain a bit more?
>>
>>My point is, there should be not such ID exposed over netlink
>>You don't need to expose it to userspace. The user has well defined
>>handle as you agreed with above. For example:
>>
>>ice/c92d02a7129f4747/1
>>ice/c92d02a7129f4747/2
>>
>>This is shown in dpll device GET/DUMP outputs.
>>Also user passes it during SET operation:
>>$ dplltool set ice/c92d02a7129f4747/1 mode auto
>>
>>Isn't that enough stable and nice?
>>
>
>I agree with Vadim, this is rather to be used by a daemon tools, which
>would get the index once, then could use it as long as device is there.

So basically you say, you can have 2 approaches in app:
1)
id = dpll_device_get_id("ice/c92d02a7129f4747/1")
dpll_device_set(id, something);
dpll_device_set(id, something);
dpll_device_set(id, something);
2):
dpll_device_set("ice/c92d02a7129f4747/1, something);
dpll_device_set("ice/c92d02a7129f4747/1, something);
dpll_device_set("ice/c92d02a7129f4747/1, something);

What is exactly benefit of the first one? Why to have 2 handles? Devlink
is a nice example of 2) approach, no problem there.

Perhaps I'm missing something, but looks like you want the id for no
good reason and this dual-handle scheme just makes things more
complicated with 0 added value.


>
>>
>>>
>>>>
>>>> > +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>> > +			 struct netlink_ext_ack *extack)
>>>> > +{
>>>> > +	struct dpll_pin_ref *ref = NULL;
>>>>
>>>> Why this needs to be initialized?
>>>>
>>>No need, fixed.
>>>
>>>
>>>>
>>>> > +	enum dpll_pin_state state;
>>>> > +	struct nlattr *nest;
>>>> > +	unsigned long index;
>>>> > +	int ret;
>>>> > +
>>>> > +	xa_for_each(&pin->parent_refs, index, ref) {
>>>> > +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>> > +			return -EFAULT;
>>>> > +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>> > +				ref->pin->dev_driver_id)) {
>>>> > +			ret = -EMSGSIZE;
>>>> > +			goto nest_cancel;
>>>> > +		}
>>>> > +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>> > +			ret = -EMSGSIZE;
>>>> > +			goto nest_cancel;
>>>> > +		}
>>>> > +		nla_nest_end(msg, nest);
>>>> > +	}
>>>>
>>>> How is this function different to dpll_msg_add_pin_parents()?
>>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>>> hard to follow for me :/
>>>>
>>>> Did you get lost here as well? If yes, this needs some serious think
>>>> through :)
>>>>
>>>
>>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>>relationship between these things?
>>
>
>[ copy paste my answer from previous response ]
>No, it is just leftover I didn't catch, we can leave one function and use it in
>both cases. Sorry about that, great catch!

Hmm, ok. I'm still lost a bit with all the referencing and cross
referencing, I wonder if it could be made a bit simpler and/or easier to
read and follow.


>
>[...]
>
>>>> > +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>>
>>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>>> code.
>>>>
>>>
>>>I believe it's INDEX which is provided by the driver. I'll think about
>>>renaming,
>>>but suggestions are welcome.
>>
>>Let's use "index" and "INDEX" internalla and in Netlink attr names as
>>well then.
>>
>
>For me makes sense to have a common name instead of origin-based one.

What do you mean by this?


>
>>[...]
>>
>>
>>>
>>>>
>>>> > +	int rem, ret = -EINVAL;
>>>> > +	struct nlattr *a;
>>>> > +
>>>> > +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>> > +			  genlmsg_len(info->genlhdr), rem) {
>>>>
>>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>>
>>>
>>>I had some unknown crashes when I was using such access. I might have lost
>>>some checks, will try it again.
>>
>>Odd, yet definitelly debuggable though :)
>>
>>[...]
>>
>>
>>>> > +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>> > +{
>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>>
>>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>>> taking order is opposite.
>>>>
>>>
>>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>>how to
>>>solve it here. Any thoughts?
>>
>>Well, here you can just call dpll_pre_dumpit() before
>>mutex_lock(&dpll_pin_xa_lock)
>>to take the locks in the same order.
>>
>
>This double lock doesn't really improve anything.
>Any objections on having single mutex/lock for access the dpll subsystem?

Yeah, we had it in devlink and then we spent a lot of a time to get rid
of it. Now we have per-instance locking. Here, it would be
per-dpll-device locking.

No strong opinion, perhaps on case of dpll one master lock is enough.
Perhaps Jakub would have some opinion on this.


>
>>
>>>
>>>>
>>>> > +
>>>> > +	return dpll_pre_dumpit(cb);
>>>> > +}
>>>> > +
>>>> > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>> > +{
>>>> > +	mutex_unlock(&dpll_pin_xa_lock);
>>>> > +
>>>> > +	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-
>>>> > dev_driver_id))
>>>> > +		return -EMSGSIZE;
>>>>
>>>> I don't really understand why you are trying figure something new and
>>>> interesting with the change notifications. This object mix and random
>>>> attrs fillup is something very wrong and makes userspace completely
>>>> fuzzy about what it is getting. And yet it is so simple:
>>>> You have 2 objects, dpll and pin, please just have:
>>>> dpll_notify()
>>>> dpll_pin_notify()
>>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>>> No need for any smartness. Have this dumb and simple.
>>>>
>>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>>
>>>But with full object-snapshot user space app will lose the information about
>>>what exactly has changed. The reason to have this event is to provide the
>>>attributes which have changed. Otherwise, the app should have full snapshot
>>>and
>>>compare all attributes to figure out changes and that's might not be great
>>>idea.
>>
>>Wait, are you saying that the app is stateless? Could you provide
>>example use cases?
>>
>>From what I see, the app managing dpll knows the state of the device and
>>pins, it monitors for the changes and saves new state with appropriate
>>reaction (might be some action or maybe just log entry).
>>
>
>It depends on the use case, right? App developer having those information knows
>what has changed, thus can react in a way it thinks is most suitable.
>IMHO from user perspective it is good to have a notification which actually
>shows it's reason, so proper flow could be assigned to handle the reaction.

Again, could you provide me specific example in which this is needed?
I may be missing something, but I don't see how it can bring
and benefit. It just makes the live of the app harder because it has to
treat the get and notify messages differently.

It is quite common for app to:
init:
1) get object state
2) store it
3) apply configuration
runtime:
1) listen to object state change
2) store it
3) apply configuration

Same code for both.




>
>>
>>>
>>>>
>>>> > +
>>>> > +	switch (attr) {
>>>> > +	case DPLL_A_MODE:
>>>> > +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>>> > +		break;
>>>> > +	case DPLL_A_SOURCE_PIN_IDX:
>>>> > +		ret = dpll_msg_add_source_pin_idx(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:
>>>> > +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>>> > +		break;
>>>> > +	case DPLL_A_PIN_PRIO:
>>>> > +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>> > +		if (!ref)
>>>> > +			return -EFAULT;
>>>> > +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>>> > +		break;
>>>> > +	case DPLL_A_PIN_STATE:
>>>> > +		if (parent) {
>>>> > +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>>> > +			if (!ref)
>>>> > +				return -EFAULT;
>>>> > +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>>> > +				return -EOPNOTSUPP;
>>>> > +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>>> > +							 NULL);
>>>> > +			if (ret)
>>>> > +				return ret;
>>>> > +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>>> > +					parent->dev_driver_id))
>>>> > +				return -EMSGSIZE;
>>>> > +		} else {
>>>> > +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>> > +			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);
>>>>
>>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>>> I have to be missing something.
>>>
>>>Changes might come from other places, but will affect the DPLL device and we
>>>have to notify users in this case.
>>
>>I'm not sure I follow. There are 2 scenarios for change:
>>1) user originated - user issues set of something
>>2) driver originated - something changes in HW, driver propagates that
>>
>>With what I suggest, both scenarios work of course. My point is, user
>>knows very well the objects: device and pin, he knows the format or
>>messages that are related to GET/DUMP/SET operations of both. The
>>notification should have the same format, that is all I say.
>>
>>Btw, you can see devlink code for example how the notifications like
>>this are implemented and work.
>>
>
>Devlink packs all the info into a netlink message and notifies with it, isn't
>it that it has all the info "buffered" in its structures?

Not really. Ops are there as well.


>A dpll subsystem keeps only some of the info in its internal structures, but
>for most of it it has to question the driver. It would do it multiple times
>i.e. if there would be a change on a pin connected to multiple dpll devices.
>
>With current approach the notifications are pretty light to the subsystem as
>well as for the registered clients, and as you suggested with having info of
>what actually changed the userspace could implement a stateless daemon right
>away.

Okay, examples? I really can't see how this stateless deamon could be
implemented, but perhaps I lack better imagination :/


>
>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] 202+ messages in thread

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

Tue, Mar 14, 2023 at 05:43:18PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>Sent: Tuesday, March 14, 2023 12:00 AM
>>
>
>[...]
>
>>>> +
>>>> +/**
>>>> + * dpll_device_get - find existing or create new dpll device
>>>> + * @clock_id: clock_id of creator
>>>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>>>> +{
>>>> +	struct dpll_device *dpll, *ret = NULL;
>>>> +	unsigned long index;
>>>> +
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>> +		if (dpll->clock_id == clock_id &&
>>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>>
>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>> identification, isn't that enough for you.
>>
>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>driver
>>implementation there are 2 different DPLLs - to recover from PPS input and
>>to
>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>is
>>the same for both of them. But Arkadiusz can tell more about it.
>
>Yes, exactly.
>One driver can have multiple dplls with the same clock id.
>Actually dev_driver_id makes dpll objects unique.
>
>>>
>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>
>>Simply id doesn't tell anything either. dpll_dev_id?
>
>Looks good to me.

Let's call this "device_index" and "pin_index" for the pin getter as I
suggested in the other email.


>
>>
>>>> +		    dpll->module == module) {
>>>> +			ret = dpll;
>>>> +			refcount_inc(&ret->refcount);
>>>> +			break;
>>>> +		}
>>>> +	}
>>>> +	if (!ret)
>>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>> +	mutex_unlock(&dpll_device_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;
>>>
>>> Remove this check. The driver should not call this with NULL.
>>
>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>
>
>I agree, IMHO it is better to have safety checks :)

IDK, we should rely on basic driver sanity. Let least put a WARN_ON() to
these checks. But try to reduce them at least.



>
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>
>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>
>>Good point!
>>
>
>Yes, great point!
>
>>>> +		xa_destroy(&dpll->pin_refs);
>>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>>> +		kfree(dpll);
>>>> +	}
>>>> +	mutex_unlock(&dpll_device_xa_lock);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>> +
>>>> +/**
>>>> + * 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,
>>>> +			 struct dpll_device_ops *ops, void *priv,
>>>> +			 struct device *owner)
>>>> +{
>>>> +	if (WARN_ON(!ops || !owner))
>>>> +		return -EINVAL;
>>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>> +		return -EINVAL;
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>> +		mutex_unlock(&dpll_device_xa_lock);
>>>> +		return -EEXIST;
>>>> +	}
>>>> +	dpll->dev.bus = owner->bus;
>>>> +	dpll->parent = owner;
>>>> +	dpll->type = type;
>>>> +	dpll->ops = ops;
>>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>> +		     dpll->dev_driver_id);
>>>
>>> This is really odd. As a result, the user would see something like:
>>> pci/0000:01:00.0_1
>>> pci/0000:01:00.0_2
>>>
>>> I have to say it is confusing. In devlink, is bus/name and the user
>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>> there. Also, "_" might have some meaning on some bus. Should not
>>> concatename dev_name() with anything.
>>>
>>> Thinking about this some more, the module/clock_id tuple should be
>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>> as the user handle, can't it?
>>> Example:
>>> ice/c92d02a7129f4747
>>> mlx5/90265d8bf6e6df56
>>>
>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>> enough), you can put it here as well:
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>
>>Looks good, will change it
>
>Makes sense to me.

Good. Fits the mlx5 model nicely.


>
>>
>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>> share instance of DPLL equally, there is no "one clock master". >
>>>> +	dpll->priv = priv;
>>>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>> +	mutex_unlock(&dpll_device_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
>>>> + *
>>>> + * Deregister device, make it unavailable for userspace.
>>>> + * Note: It does not free the memory
>>>> + */
>>>> +void dpll_device_unregister(struct dpll_device *dpll)
>>>> +{
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	ASSERT_DPLL_REGISTERED(dpll);
>>>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>> +	mutex_unlock(&dpll_device_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
>>>> + * @dev_driver_id: id 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
>>>
>>> Extra "*"'s
>>
>>Ok, I can re-format the comments across the files.
>
>This is expected on enumerating return values in kernel-docs:
>https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#Return%20values

Ah, okay. I missed that. Sorry.


>
>>
>>>> + */
>>>> +struct dpll_pin *
>>>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module
>>*module,
>>>
>>> Odd whitespace.
>>>
>>> Also, func should be static.
>>>
>>
>>Fixed.
>>
>>>
>>>> +	       const struct dpll_pin_properties *prop)
>>>> +{
>>>> +	struct dpll_pin *pin;
>>>> +	int ret;
>>>> +
>>>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>>>> +	if (!pin)
>>>> +		return ERR_PTR(-ENOMEM);
>>>> +	pin->dev_driver_id = device_drv_id;
>>>
>>> Name inconsistency: driver/drv
>>> you have it on multiple places
>>>
>>
>>Changed it every where, thanks for spotting.
>>
>>>
>>>> +	pin->clock_id = clock_id;
>>>> +	pin->module = module;
>>>> +	refcount_set(&pin->refcount, 1);
>>>> +	if (WARN_ON(!prop->description)) {
>>>> +		ret = -EINVAL;
>>>> +		goto release;
>>>> +	}
>>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>> +	if (!pin->prop.description) {
>>>> +		ret = -ENOMEM;
>>>> +		goto release;
>>>> +	}
>>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>> +		ret = -EINVAL;
>>>> +		goto release;
>>>> +	}
>>>> +	pin->prop.type = prop->type;
>>>> +	pin->prop.capabilities = prop->capabilities;
>>>> +	pin->prop.freq_supported = prop->freq_supported;
>>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>>
>>> Make sure that the driver maintains prop (static const) and just save
>>> the pointer. Prop does not need to be something driver needs to change.
>>>
>>
>>What's the difference? For ptp_ocp, we have the same configuration for all
>>ext pins and the allocator only changes the name of the pin. Properties of
>>the DPLL pins are stored within the pin object, not the driver, in this
>>case.
>>Not sure if the pointer way is much better...
>>
>
>I also don't feel it.
>Dpll subsystem directly using memory of different driver doesn't look like a
>great design.

Wait. This is done all the time in kernel. Almost every ops for example.
Lots of other examples like that as well:
git grep "static const struct" drivers/net/


>
>>>
>>>> +	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->idx, pin,
>>>> +		       xa_limit_16b, GFP_KERNEL);
>>>> +release:
>>>> +	if (ret) {
>>>> +		xa_destroy(&pin->dpll_refs);
>>>> +		xa_destroy(&pin->parent_refs);
>>>> +		kfree(pin->prop.description);
>>>> +		kfree(pin->rclk_dev_name);
>>>> +		kfree(pin);
>>>> +		return ERR_PTR(ret);
>>>> +	}
>>>> +
>>>> +	return pin;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpll_pin_get - find existing or create new dpll pin
>>>> + * @clock_id: clock_id of creator
>>>> + * @dev_driver_id: id 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
>>>
>>> This is one example, I'm pretty sure that there are others, when you
>>> have text inconsistencies in func doc for the same function in .c and .h
>>> Have it please only on one place. .c is the usual place.
>>>
>>
>>Yep, will clear .h files.
>>
>
>There might be some issues, and certainly makes sense to have them in one
>place.
>
>>>
>>>> + */
>>>> +struct dpll_pin *
>>>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
>>>
>>> Again, why do you need this device_drv_id? Clock id should be enough.
>>>
>>I explained the reason earlier, but the naming is fixed.
>
>Yes, this device_drv_id is id of a pin not a dpll device id.
>Maybe worth to rename it to make it more clear.

Yeah, device_index and pin_index


>
>>
>>>
>>>> +	     const struct dpll_pin_properties *prop)
>>>> +{
>>>> +	struct dpll_pin *pos, *ret = NULL;
>>>> +	unsigned long index;
>>>> +
>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>> +		if (pos->clock_id == clock_id &&
>>>> +		    pos->dev_driver_id == device_drv_id &&
>>>> +		    pos->module == module) {
>>>
>>> Compare prop as well.
>>>
>>> Can't the driver_id (pin index) be something const as well? I think it
>>> should. And therefore it could be easily put inside.
>>>
>>
>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>
>Yes, they should be unique for a single pin object, and enough for getting
>pin reference.
>Basically if driver would call twice this with different props, it would be
>broken.
>We could have props compared here as well.

Let's leave it as it is for now, it's ok.


>
>>And
>>now I think that *prop is not needed here at all. Arkadiusz, any thoughts?
>
>As dpll_pin_alloc is having them assigned to a pin object, thus had to put it
>here. Not sure how to solve it differently.
>Jiri's suggestion to put dev_driver_id inside of pin props would also work
>AFAIU.

Let's have it as device_get()/pin_get() args as you have it right now.
Makes more sense as the prop could be static const as I described in the
other email.


>
>[...]
>
>>>> +
>>>> +/**
>>>> + * 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,
>>>> +		  struct dpll_pin_ops *ops, void *priv,
>>>> +		  struct device *rclk_device)
>>>
>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>
>>>
>>>> +{
>>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>>
>>> If you need to store something here, store the pointer to the device
>>> directly. But this rclk_device seems odd to me.
>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>> is incomplete. What should it server for?
>>>
>>
>>Well, these questions go to Arkadiusz...
>>
>
>If pin is able to recover signal from some device this shall convey that
>device struct pointer.
>Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>attribute.
>Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>part. But isn't it better to have the name ready to use there?
>
>It might be incomplete only if one device would have some kind of access to
>a different bus? I don't think it is valid use case.

Very valid as I explained in the other email.


>
>Basically the driver will refer only to the devices handled by that driver,
>which means if dpll is on some bus, also all the pins are there, didn't notice
>the need to have bus here as well.
>
>[...]
>
>>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>> +{
>>>> +	struct dpll_pin_ref *pos;
>>>> +	unsigned long i;
>>>> +
>>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>
>>> How exactly pos->pin could be NULL?
>>>
>
>I believe if proper synchronization in place it shall not be NULL, I left it
>after fixing some issue with access to the pin that was already removed..

Then don't check it here.


>
>>> Also, you are degrading the xarray to a mere list here with lookup like
>>> this. Why can't you use the pin index coming from driver and
>>> insert/lookup based on this index?
>>>
>>Good point. We just have to be sure, that drivers provide 0-based indexes
>>for their pins. I'll re-think it.
>>
>
>After quick thinking, it might be doable, storing pin being registered on
>dpll->pin_refs under given by driver pin's dev_driver_idx or whatever it would
>be named.

Yep.


>
>>
>>>
>>>> +			return pos->pin;
>>>> +	}
>>>> +
>>>> +	return NULL;
>>>> +}
>>>> +
>>>> +/**
>>>> + * 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)
>>>> +{
>>>> +	return dpll->priv;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(dpll_priv);
>>>> +
>>>> +/**
>>>> + * 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,
>>>
>>> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
>>> Just pass the priv directly to the op and avoid need for this helper,
>>> no? Same goes to the rest of the priv helpers.
>>>
>
>No strong opinion, probably doable.

It is better for the driver writer to actually see right away that
there is a priv so he can use it and not need to get the priv from
some place else (using pin index * lookup). Very nice example where
this would work is the last patch of this set, where Vadim does not
use priv at all.


>
>>>
>>>> +			    const struct dpll_pin *pin)
>>>> +{
>>>> +	struct dpll_pin_ref *ref;
>>>> +
>>>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
>>>
>>> Why cast is needed here? You have this on multiple places.
>>>
>
>`const` of struct dpll_pin *pin makes a warning/error there, there is something
>broken on xarray implementation of for_each I believe.

Either fix it or avoid using const arg. Cast's like this always smell
and should be avoided.


>
>[...]
>
>>>> +
>>>> +static int
>>>> +dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>> +			 struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct dpll_pin_ref *ref = NULL;
>>>
>>> Why this needs to be initialized?
>>>
>>No need, fixed.
>>
>>
>>>
>>>> +	enum dpll_pin_state state;
>>>> +	struct nlattr *nest;
>>>> +	unsigned long index;
>>>> +	int ret;
>>>> +
>>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>> +			return -EFAULT;
>>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>> +				ref->pin->dev_driver_id)) {
>>>> +			ret = -EMSGSIZE;
>>>> +			goto nest_cancel;
>>>> +		}
>>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>> +			ret = -EMSGSIZE;
>>>> +			goto nest_cancel;
>>>> +		}
>>>> +		nla_nest_end(msg, nest);
>>>> +	}
>>>
>>> How is this function different to dpll_msg_add_pin_parents()?
>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>> hard to follow for me :/
>>>
>>> Did you get lost here as well? If yes, this needs some serious think
>>> through :)
>>>
>>
>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>relationship between these things?
>>
>
>No, it is just leftover I didn't catch, we can leave one function and use it in
>both cases. Sorry about that, great catch!
>
>>>
>>>> +
>>>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>>> +			 struct dpll_device *dpll,
>>>> +			 struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct dpll_pin_ref *ref;
>>>> +	int ret;
>>>> +
>>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>
>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>> code.
>>>
>>
>>I believe it's INDEX which is provided by the driver. I'll think about
>>renaming,
>>but suggestions are welcome.
>
>Yes, confusing a bit, I agree we need a fix of this.
>Isn't it that dpll has an INDEX assigned by dpll subsystem on its allocation
>and pin have ID given by the driver?

Driver should provice the device_index and pin_index in appropriate
_get() functions. I think it is better to spell out "index". "idx" looks
a bit odd. Either way, please unify the name all along the code, netlink
included.

ID is your odd dual-handle construct, which I don't see a need for and
only adds confusions. Better to remove it.


>
>[...]
>
>>>> +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;
>>>> +	u32 parent_idx = PIN_IDX_INVALID;
>>>
>>> You just need this PIN_IDX_INVALID define internally in this function,
>>> change the flow to avoid a need for it.
>>>
>>
>>I'll re-think it, thanks.
>>
>>>
>>>> +	int rem, ret = -EINVAL;
>>>> +	struct nlattr *a;
>>>> +
>>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>> +			  genlmsg_len(info->genlhdr), rem) {
>>>
>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>
>>
>>I had some unknown crashes when I was using such access. I might have lost
>>some
>>checks, will try it again.
>>
>>>
>>>> +		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_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) {
>>>
>>> Again, change the flow to:
>>> 	if (attrs[DPLL_A_PIN_STATE]) {
>>>
>>> and avoid need for this value set/check.
>>>
>>
>>Yep, will try.
>
>Yes, this shall work now, as long as there are no multiple nested attributes
>coming from userspace.
>
>[...]
>
>>>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct
>>>>sk_buff *skb,
>>>> +			struct genl_info *info)
>>>> +{
>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>> +	dpll_post_doit(ops, skb, info);
>>>> +}
>>>> +
>>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>> +{
>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>
>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>> taking order is opposite.
>>>
>>
>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>how to
>>solve it here. Any thoughts?
>>
>
>Not really, this is why it is there :(
>A single global lock for whole dpll subsystem?

Either that or per-instance/device lock.


>
>>>
>>>> +
>>>> +	return dpll_pre_dumpit(cb);
>>>> +}
>>>> +
>>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>> +{
>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>> +
>>>> +	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->dev_driver_id))
>>>> +		return -EMSGSIZE;
>>>
>>> I don't really understand why you are trying figure something new and
>>> interesting with the change notifications. This object mix and random
>>> attrs fillup is something very wrong and makes userspace completely
>>> fuzzy about what it is getting. And yet it is so simple:
>>> You have 2 objects, dpll and pin, please just have:
>>> dpll_notify()
>>> dpll_pin_notify()
>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>> No need for any smartness. Have this dumb and simple.
>>>
>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>
>>But with full object-snapshot user space app will lose the information
>>about
>>what exactly has changed. The reason to have this event is to provide the
>>attributes which have changed. Otherwise, the app should have full snapshot
>>and
>>compare all attributes to figure out changes and that's might not be great
>>idea.
>>
>
>I agree that from functional perspective it is better to have on userspace a
>reason of the notification.

I would like to understand why exactly is it better.
But anyway, I was thinking about it a bit more and it might make
sense only the added/changed/deleted attribute to safe some msg space
and getting/parsing cycles. However, it is questionable how much does it
actually save. But you apparently strongly want it, so lets have it

But, the format of the message should be exactly the same as for GET.
Meaning, if some nested attr changes/is added/is removed, the msg should
contain the proper nest same as for the same attr in GET msg. Think of
it as if you assemble the whole GET msg for an object and filter out
things that did not change.

Also need to emphasize strict objects separation, same as for GET.

Also, you have to distinguish between attr being added/removed or the
whole object being added/removed. The userspace has to understand
difference.

So you would have events like:
DEVICE_ADDED
DEVICE_REMOVED
DEVICE_ATTRS_ADDED
DEVICE_ATTRS_CHANGED
DEVICE_ATTRS_REMOVED
PIN_ADDED
PIN_REMOVED
PIN_ATTRS_ADDED
PIN_ATTRS_CHANGED
PIN_ATTRS_REMOVED

This scenario I can imagine working in a sane way.

Makes sense?

Btw, with the whole object snapshot scenario you just have:
DEVICE_NEW (sent for creation and change)
DEVICE_DEL
PIN_NEW (sent for creation and change)
PIN_DEL
And you are fine with it. This is how it's usually done in Netlink.
In fact, do you have examples in kernel code of what you are suggesting?



>
>[...]
>
>Thank you,
>Arkadiusz
>

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

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

Tue, Mar 14, 2023 at 05:43:18PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>Sent: Tuesday, March 14, 2023 12:00 AM
>>
>
>[...]
>
>>>> +
>>>> +/**
>>>> + * dpll_device_get - find existing or create new dpll device
>>>> + * @clock_id: clock_id of creator
>>>> + * @dev_driver_id: id given by dev 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 dev_driver_id, struct module *module)
>>>> +{
>>>> +	struct dpll_device *dpll, *ret = NULL;
>>>> +	unsigned long index;
>>>> +
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>> +		if (dpll->clock_id == clock_id &&
>>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>>
>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>> identification, isn't that enough for you.
>>
>>dev_driver_id is needed to provide several DPLLs from one device. In ice
>>driver
>>implementation there are 2 different DPLLs - to recover from PPS input and
>>to
>>recover from Sync-E. I believe there is only one clock, that's why clock id
>>is
>>the same for both of them. But Arkadiusz can tell more about it.
>
>Yes, exactly.
>One driver can have multiple dplls with the same clock id.
>Actually dev_driver_id makes dpll objects unique.
>
>>>
>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>
>>Simply id doesn't tell anything either. dpll_dev_id?
>
>Looks good to me.

Let's call this "device_index" and "pin_index" for the pin getter as I
suggested in the other email.


>
>>
>>>> +		    dpll->module == module) {
>>>> +			ret = dpll;
>>>> +			refcount_inc(&ret->refcount);
>>>> +			break;
>>>> +		}
>>>> +	}
>>>> +	if (!ret)
>>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>> +	mutex_unlock(&dpll_device_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;
>>>
>>> Remove this check. The driver should not call this with NULL.
>>
>>Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>
>
>I agree, IMHO it is better to have safety checks :)

IDK, we should rely on basic driver sanity. Let least put a WARN_ON() to
these checks. But try to reduce them at least.



>
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>
>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>
>>Good point!
>>
>
>Yes, great point!
>
>>>> +		xa_destroy(&dpll->pin_refs);
>>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>>> +		kfree(dpll);
>>>> +	}
>>>> +	mutex_unlock(&dpll_device_xa_lock);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>> +
>>>> +/**
>>>> + * 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,
>>>> +			 struct dpll_device_ops *ops, void *priv,
>>>> +			 struct device *owner)
>>>> +{
>>>> +	if (WARN_ON(!ops || !owner))
>>>> +		return -EINVAL;
>>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>> +		return -EINVAL;
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>> +		mutex_unlock(&dpll_device_xa_lock);
>>>> +		return -EEXIST;
>>>> +	}
>>>> +	dpll->dev.bus = owner->bus;
>>>> +	dpll->parent = owner;
>>>> +	dpll->type = type;
>>>> +	dpll->ops = ops;
>>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>> +		     dpll->dev_driver_id);
>>>
>>> This is really odd. As a result, the user would see something like:
>>> pci/0000:01:00.0_1
>>> pci/0000:01:00.0_2
>>>
>>> I have to say it is confusing. In devlink, is bus/name and the user
>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>> there. Also, "_" might have some meaning on some bus. Should not
>>> concatename dev_name() with anything.
>>>
>>> Thinking about this some more, the module/clock_id tuple should be
>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>> as the user handle, can't it?
>>> Example:
>>> ice/c92d02a7129f4747
>>> mlx5/90265d8bf6e6df56
>>>
>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>> enough), you can put it here as well:
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>
>>Looks good, will change it
>
>Makes sense to me.

Good. Fits the mlx5 model nicely.


>
>>
>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>> share instance of DPLL equally, there is no "one clock master". >
>>>> +	dpll->priv = priv;
>>>> +	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>> +	mutex_unlock(&dpll_device_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
>>>> + *
>>>> + * Deregister device, make it unavailable for userspace.
>>>> + * Note: It does not free the memory
>>>> + */
>>>> +void dpll_device_unregister(struct dpll_device *dpll)
>>>> +{
>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>> +	ASSERT_DPLL_REGISTERED(dpll);
>>>> +	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>> +	mutex_unlock(&dpll_device_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
>>>> + * @dev_driver_id: id 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
>>>
>>> Extra "*"'s
>>
>>Ok, I can re-format the comments across the files.
>
>This is expected on enumerating return values in kernel-docs:
>https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#Return%20values

Ah, okay. I missed that. Sorry.


>
>>
>>>> + */
>>>> +struct dpll_pin *
>>>> +dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module
>>*module,
>>>
>>> Odd whitespace.
>>>
>>> Also, func should be static.
>>>
>>
>>Fixed.
>>
>>>
>>>> +	       const struct dpll_pin_properties *prop)
>>>> +{
>>>> +	struct dpll_pin *pin;
>>>> +	int ret;
>>>> +
>>>> +	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>>>> +	if (!pin)
>>>> +		return ERR_PTR(-ENOMEM);
>>>> +	pin->dev_driver_id = device_drv_id;
>>>
>>> Name inconsistency: driver/drv
>>> you have it on multiple places
>>>
>>
>>Changed it every where, thanks for spotting.
>>
>>>
>>>> +	pin->clock_id = clock_id;
>>>> +	pin->module = module;
>>>> +	refcount_set(&pin->refcount, 1);
>>>> +	if (WARN_ON(!prop->description)) {
>>>> +		ret = -EINVAL;
>>>> +		goto release;
>>>> +	}
>>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>> +	if (!pin->prop.description) {
>>>> +		ret = -ENOMEM;
>>>> +		goto release;
>>>> +	}
>>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>> +		ret = -EINVAL;
>>>> +		goto release;
>>>> +	}
>>>> +	pin->prop.type = prop->type;
>>>> +	pin->prop.capabilities = prop->capabilities;
>>>> +	pin->prop.freq_supported = prop->freq_supported;
>>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>>
>>> Make sure that the driver maintains prop (static const) and just save
>>> the pointer. Prop does not need to be something driver needs to change.
>>>
>>
>>What's the difference? For ptp_ocp, we have the same configuration for all
>>ext pins and the allocator only changes the name of the pin. Properties of
>>the DPLL pins are stored within the pin object, not the driver, in this
>>case.
>>Not sure if the pointer way is much better...
>>
>
>I also don't feel it.
>Dpll subsystem directly using memory of different driver doesn't look like a
>great design.

Wait. This is done all the time in kernel. Almost every ops for example.
Lots of other examples like that as well:
git grep "static const struct" drivers/net/


>
>>>
>>>> +	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->idx, pin,
>>>> +		       xa_limit_16b, GFP_KERNEL);
>>>> +release:
>>>> +	if (ret) {
>>>> +		xa_destroy(&pin->dpll_refs);
>>>> +		xa_destroy(&pin->parent_refs);
>>>> +		kfree(pin->prop.description);
>>>> +		kfree(pin->rclk_dev_name);
>>>> +		kfree(pin);
>>>> +		return ERR_PTR(ret);
>>>> +	}
>>>> +
>>>> +	return pin;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dpll_pin_get - find existing or create new dpll pin
>>>> + * @clock_id: clock_id of creator
>>>> + * @dev_driver_id: id 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
>>>
>>> This is one example, I'm pretty sure that there are others, when you
>>> have text inconsistencies in func doc for the same function in .c and .h
>>> Have it please only on one place. .c is the usual place.
>>>
>>
>>Yep, will clear .h files.
>>
>
>There might be some issues, and certainly makes sense to have them in one
>place.
>
>>>
>>>> + */
>>>> +struct dpll_pin *
>>>> +dpll_pin_get(u64 clock_id, u32 device_drv_id, struct module *module,
>>>
>>> Again, why do you need this device_drv_id? Clock id should be enough.
>>>
>>I explained the reason earlier, but the naming is fixed.
>
>Yes, this device_drv_id is id of a pin not a dpll device id.
>Maybe worth to rename it to make it more clear.

Yeah, device_index and pin_index


>
>>
>>>
>>>> +	     const struct dpll_pin_properties *prop)
>>>> +{
>>>> +	struct dpll_pin *pos, *ret = NULL;
>>>> +	unsigned long index;
>>>> +
>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>> +		if (pos->clock_id == clock_id &&
>>>> +		    pos->dev_driver_id == device_drv_id &&
>>>> +		    pos->module == module) {
>>>
>>> Compare prop as well.
>>>
>>> Can't the driver_id (pin index) be something const as well? I think it
>>> should. And therefore it could be easily put inside.
>>>
>>
>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>
>Yes, they should be unique for a single pin object, and enough for getting
>pin reference.
>Basically if driver would call twice this with different props, it would be
>broken.
>We could have props compared here as well.

Let's leave it as it is for now, it's ok.


>
>>And
>>now I think that *prop is not needed here at all. Arkadiusz, any thoughts?
>
>As dpll_pin_alloc is having them assigned to a pin object, thus had to put it
>here. Not sure how to solve it differently.
>Jiri's suggestion to put dev_driver_id inside of pin props would also work
>AFAIU.

Let's have it as device_get()/pin_get() args as you have it right now.
Makes more sense as the prop could be static const as I described in the
other email.


>
>[...]
>
>>>> +
>>>> +/**
>>>> + * 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,
>>>> +		  struct dpll_pin_ops *ops, void *priv,
>>>> +		  struct device *rclk_device)
>>>
>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>
>>>
>>>> +{
>>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) : NULL;
>>>
>>> If you need to store something here, store the pointer to the device
>>> directly. But this rclk_device seems odd to me.
>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>> is incomplete. What should it server for?
>>>
>>
>>Well, these questions go to Arkadiusz...
>>
>
>If pin is able to recover signal from some device this shall convey that
>device struct pointer.
>Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>attribute.
>Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>part. But isn't it better to have the name ready to use there?
>
>It might be incomplete only if one device would have some kind of access to
>a different bus? I don't think it is valid use case.

Very valid as I explained in the other email.


>
>Basically the driver will refer only to the devices handled by that driver,
>which means if dpll is on some bus, also all the pins are there, didn't notice
>the need to have bus here as well.
>
>[...]
>
>>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>> +{
>>>> +	struct dpll_pin_ref *pos;
>>>> +	unsigned long i;
>>>> +
>>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>
>>> How exactly pos->pin could be NULL?
>>>
>
>I believe if proper synchronization in place it shall not be NULL, I left it
>after fixing some issue with access to the pin that was already removed..

Then don't check it here.


>
>>> Also, you are degrading the xarray to a mere list here with lookup like
>>> this. Why can't you use the pin index coming from driver and
>>> insert/lookup based on this index?
>>>
>>Good point. We just have to be sure, that drivers provide 0-based indexes
>>for their pins. I'll re-think it.
>>
>
>After quick thinking, it might be doable, storing pin being registered on
>dpll->pin_refs under given by driver pin's dev_driver_idx or whatever it would
>be named.

Yep.


>
>>
>>>
>>>> +			return pos->pin;
>>>> +	}
>>>> +
>>>> +	return NULL;
>>>> +}
>>>> +
>>>> +/**
>>>> + * 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)
>>>> +{
>>>> +	return dpll->priv;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(dpll_priv);
>>>> +
>>>> +/**
>>>> + * 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,
>>>
>>> IIUC, you use this helper from dpll ops in drivers to get per dpll priv.
>>> Just pass the priv directly to the op and avoid need for this helper,
>>> no? Same goes to the rest of the priv helpers.
>>>
>
>No strong opinion, probably doable.

It is better for the driver writer to actually see right away that
there is a priv so he can use it and not need to get the priv from
some place else (using pin index * lookup). Very nice example where
this would work is the last patch of this set, where Vadim does not
use priv at all.


>
>>>
>>>> +			    const struct dpll_pin *pin)
>>>> +{
>>>> +	struct dpll_pin_ref *ref;
>>>> +
>>>> +	ref = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
>>>
>>> Why cast is needed here? You have this on multiple places.
>>>
>
>`const` of struct dpll_pin *pin makes a warning/error there, there is something
>broken on xarray implementation of for_each I believe.

Either fix it or avoid using const arg. Cast's like this always smell
and should be avoided.


>
>[...]
>
>>>> +
>>>> +static int
>>>> +dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>> +			 struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct dpll_pin_ref *ref = NULL;
>>>
>>> Why this needs to be initialized?
>>>
>>No need, fixed.
>>
>>
>>>
>>>> +	enum dpll_pin_state state;
>>>> +	struct nlattr *nest;
>>>> +	unsigned long index;
>>>> +	int ret;
>>>> +
>>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>> +			return -EFAULT;
>>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>> +				ref->pin->dev_driver_id)) {
>>>> +			ret = -EMSGSIZE;
>>>> +			goto nest_cancel;
>>>> +		}
>>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>> +			ret = -EMSGSIZE;
>>>> +			goto nest_cancel;
>>>> +		}
>>>> +		nla_nest_end(msg, nest);
>>>> +	}
>>>
>>> How is this function different to dpll_msg_add_pin_parents()?
>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>> hard to follow for me :/
>>>
>>> Did you get lost here as well? If yes, this needs some serious think
>>> through :)
>>>
>>
>>Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>relationship between these things?
>>
>
>No, it is just leftover I didn't catch, we can leave one function and use it in
>both cases. Sorry about that, great catch!
>
>>>
>>>> +
>>>> +	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_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>>> +			 struct dpll_device *dpll,
>>>> +			 struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct dpll_pin_ref *ref;
>>>> +	int ret;
>>>> +
>>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>
>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>> code.
>>>
>>
>>I believe it's INDEX which is provided by the driver. I'll think about
>>renaming,
>>but suggestions are welcome.
>
>Yes, confusing a bit, I agree we need a fix of this.
>Isn't it that dpll has an INDEX assigned by dpll subsystem on its allocation
>and pin have ID given by the driver?

Driver should provice the device_index and pin_index in appropriate
_get() functions. I think it is better to spell out "index". "idx" looks
a bit odd. Either way, please unify the name all along the code, netlink
included.

ID is your odd dual-handle construct, which I don't see a need for and
only adds confusions. Better to remove it.


>
>[...]
>
>>>> +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;
>>>> +	u32 parent_idx = PIN_IDX_INVALID;
>>>
>>> You just need this PIN_IDX_INVALID define internally in this function,
>>> change the flow to avoid a need for it.
>>>
>>
>>I'll re-think it, thanks.
>>
>>>
>>>> +	int rem, ret = -EINVAL;
>>>> +	struct nlattr *a;
>>>> +
>>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>> +			  genlmsg_len(info->genlhdr), rem) {
>>>
>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>
>>
>>I had some unknown crashes when I was using such access. I might have lost
>>some
>>checks, will try it again.
>>
>>>
>>>> +		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_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) {
>>>
>>> Again, change the flow to:
>>> 	if (attrs[DPLL_A_PIN_STATE]) {
>>>
>>> and avoid need for this value set/check.
>>>
>>
>>Yep, will try.
>
>Yes, this shall work now, as long as there are no multiple nested attributes
>coming from userspace.
>
>[...]
>
>>>> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct
>>>>sk_buff *skb,
>>>> +			struct genl_info *info)
>>>> +{
>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>> +	dpll_post_doit(ops, skb, info);
>>>> +}
>>>> +
>>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>> +{
>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>
>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>> taking order is opposite.
>>>
>>
>>Now I see an ABBA deadlock here, as well as in function before. Not sure
>>how to
>>solve it here. Any thoughts?
>>
>
>Not really, this is why it is there :(
>A single global lock for whole dpll subsystem?

Either that or per-instance/device lock.


>
>>>
>>>> +
>>>> +	return dpll_pre_dumpit(cb);
>>>> +}
>>>> +
>>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>> +{
>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>> +
>>>> +	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->dev_driver_id))
>>>> +		return -EMSGSIZE;
>>>
>>> I don't really understand why you are trying figure something new and
>>> interesting with the change notifications. This object mix and random
>>> attrs fillup is something very wrong and makes userspace completely
>>> fuzzy about what it is getting. And yet it is so simple:
>>> You have 2 objects, dpll and pin, please just have:
>>> dpll_notify()
>>> dpll_pin_notify()
>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>> No need for any smartness. Have this dumb and simple.
>>>
>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>
>>But with full object-snapshot user space app will lose the information
>>about
>>what exactly has changed. The reason to have this event is to provide the
>>attributes which have changed. Otherwise, the app should have full snapshot
>>and
>>compare all attributes to figure out changes and that's might not be great
>>idea.
>>
>
>I agree that from functional perspective it is better to have on userspace a
>reason of the notification.

I would like to understand why exactly is it better.
But anyway, I was thinking about it a bit more and it might make
sense only the added/changed/deleted attribute to safe some msg space
and getting/parsing cycles. However, it is questionable how much does it
actually save. But you apparently strongly want it, so lets have it

But, the format of the message should be exactly the same as for GET.
Meaning, if some nested attr changes/is added/is removed, the msg should
contain the proper nest same as for the same attr in GET msg. Think of
it as if you assemble the whole GET msg for an object and filter out
things that did not change.

Also need to emphasize strict objects separation, same as for GET.

Also, you have to distinguish between attr being added/removed or the
whole object being added/removed. The userspace has to understand
difference.

So you would have events like:
DEVICE_ADDED
DEVICE_REMOVED
DEVICE_ATTRS_ADDED
DEVICE_ATTRS_CHANGED
DEVICE_ATTRS_REMOVED
PIN_ADDED
PIN_REMOVED
PIN_ATTRS_ADDED
PIN_ATTRS_CHANGED
PIN_ATTRS_REMOVED

This scenario I can imagine working in a sane way.

Makes sense?

Btw, with the whole object snapshot scenario you just have:
DEVICE_NEW (sent for creation and change)
DEVICE_DEL
PIN_NEW (sent for creation and change)
PIN_DEL
And you are fine with it. This is how it's usually done in Netlink.
In fact, do you have examples in kernel code of what you are suggesting?



>
>[...]
>
>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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-15  0:10       ` Vadim Fedorenko
@ 2023-03-15 12:24         ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-15 12:24 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>On 14/03/2023 10:05, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>> > 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>> > 2 files changed, 200 insertions(+), 10 deletions(-)
>> > 
>> > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> > index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>> > --- a/drivers/ptp/ptp_ocp.c
>> > +++ b/drivers/ptp/ptp_ocp.c
>> > @@ -23,6 +23,8 @@
>> > #include <linux/mtd/mtd.h>
>> > #include <linux/nvmem-consumer.h>
>> > #include <linux/crc16.h>
>> > +#include <linux/dpll.h>
>> > +#include <uapi/linux/dpll.h>
>> 
>> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
>> 
>
>Ah, yeah, you've mentioned it for ice driver last time, but I forgot to check
>it here. Removed.
>
>> 
>> > 
>> > #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> > #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> > @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>> > 	bool	fixed_dir;
>> > 	bool	disabled;
>> > 	u8	default_fcn;
>> > +	struct dpll_pin *dpll_pin;
>> > };
>> > 
>> > struct ocp_attr_group {
>> > @@ -353,6 +356,7 @@ struct ptp_ocp {
>> > 	struct ptp_ocp_signal	signal[4];
>> > 	struct ptp_ocp_sma_connector sma[4];
>> 
>> It is quite customary to use defines for const numbers like this. Why
>> don't you do that in ptp_ocp?
>
>Historical reasons. Jonathan might answer this question.
>I will add it to my TODO list for the driver.
>
>> > 	const struct ocp_sma_op *sma_op;
>> > +	struct dpll_device *dpll;
>> > };
>> > 
>> > #define OCP_REQ_TIMESTAMP	BIT(0)
>> > @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> > }
>> > 
>> > static int
>> > -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> > {
>> > 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > -	enum ptp_ocp_sma_mode mode;
>> > -	int val;
>> > -
>> > -	mode = sma->mode;
>> > -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > -	if (val < 0)
>> > -		return val;
>> > 
>> > 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> > 		return -EOPNOTSUPP;
>> > @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > 	return val;
>> > }
>> > 
>> > +static int
>> > +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > +	enum ptp_ocp_sma_mode mode;
>> > +	int val;
>> > +
>> > +	mode = sma->mode;
>> > +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > +	if (val < 0)
>> > +		return val;
>> > +
>> > +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> > +}
>> > +
>> > static ssize_t
>> > sma1_store(struct device *dev, struct device_attribute *attr,
>> > 	   const char *buf, size_t count)
>> > @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> > 	device_unregister(&bp->dev);
>> > }
>> > 
>> > +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < 4; i++) {
>> > +		if (bp->sma[i].dpll_pin == pin)
>> > +			return i;
>> 
>> Just pass &bp->sma[i] as a priv to dpll_pin_register().
>> and get that pointer directly in pin ops. No need for lookup and use of
>> sma_nr at all.
>
>I'm still thinking about the change that you proposed to remove these
>_priv() helpers. I have to look into ice code to be sure we won't break
>any assumptions in it with moving to additional (void *) parameter.

There are basically 2 ways:
someop(struct subsystemobj *x, void *priv)
{
	struct *mine = priv;
}
or:
someop(struct subsystemobj *x)
{
	struct *mine = subsystem_get_priv(x);
}

Both are more or less equal. The first has benefit that the caller most
usually has direct access to the priv, so it can just easily pass it on.
Also, you as the driver write see right away there is a priv arg and
makes you want to use it and not figure out odd lookups to get to the
same priv.


>
>> 
>> > +	}
>> > +	return -1;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> > +				    enum dpll_lock_status *status,
>> > +				    struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> 
>> No need to cast (void *), remove it.
>> 
>Fixed everywhere in the code, thanks.
>
>
>> 
>> > +	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,
>> > +				    u32 *idx, struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +
>> > +	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,
>> > +				    u32 *mode, struct netlink_ext_ack *extack)
>> > +{
>> > +	*mode = DPLL_MODE_AUTOMATIC;
>> > +
>> 
>> No need for empty line, I believe.
>
>Ok.
>
>> > +	return 0;
>> > +}
>> > +
>> > +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> > +				    const enum dpll_mode mode,
>> > +				    struct netlink_ext_ack *extack)
>> > +{
>> > +	if (mode == DPLL_MODE_AUTOMATIC)
>> > +		return true;
>> > +
>> > +	return false;
>> 
>> Just:
>> 	return mode == DPLL_MODE_AUTOMATIC;
>> 
>Done, thanks!
>
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> > +				     const struct dpll_device *dpll,
>> > +				     enum dpll_pin_direction *direction,
>> > +				     struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	*direction = bp->sma[sma_nr].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,
>> > +				     const struct dpll_device *dpll,
>> > +				     enum dpll_pin_direction direction,
>> > +				     struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	enum ptp_ocp_sma_mode mode;
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	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,
>> > +			      const struct dpll_device *dpll,
>> > +			      const u32 frequency,
>> 
>> Why you need const for u32?
>
>No need, true, removed.
>
>> 
>> > +			      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	int val = frequency == 10000000 ? 0 : 1;
>> 
>> This is weird. I believe you should handle unsupported frequencies
>> gracefully.
>> 
>
>Currently hardware supports fixed frequencies only. That's why I have to
>do this kind of "dance". Hopefully we will improve it to support variable
>frequency.
>
>> 
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +
>> 
>> Avoid double empty lines.
>> 
>
>Yep.
>
>> 
>> > +	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> > +			      const struct dpll_device *dpll,
>> > +			      u32 *frequency,
>> > +			      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	u32 val;
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	val = bp->sma_op->get(bp, sma_nr);
>> > +	if (!val)
>> > +		*frequency = 1000000;
>> > +	else
>> > +		*frequency = 0;
>> 
>> I don't follow. How the frequency could be 0? Does that mean that the
>> pin is disabled? If yes, you should rather implement
>> .state_on_dpll_get
>> .state_on_dpll_set
>> 
>> and leave the frequency constant.
>
>It's actually a mistake. It should be 1 which means 1Hz, PPS. The value
>returned by sma_op->get is actually the type of the signal where 0 is 1PPS, 1
>is 10Mhz and so on.

So you support freq change? In the comment above you wrote "Currently
hardware supports fixed frequencies only". I'm confused.
The point is:
1) if you support freq change, you should sanitize the input properly in
   frequency_set() and return correct actual value in frequency_get()
2) if you don't support freq change, avoid filling-up these ops
   entirely.

>
>> 
>> 
>> > +	return 0;
>> > +}
>> > +
>> > +static struct dpll_device_ops dpll_ops = {
>> 
>> const
>> 
>
>Will change it together with API part. Otherwise it doesn't compile.
>
>> 
>> > +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> > +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
>> 
>> Should be called "source_pin_get" and return (struct dpll_pin *)
>> On the driver api level, no reason to pass indexes. Work with objects.
>> 
>
>Good point, will improve it.
>
>> 
>> > +	.mode_get = ptp_ocp_dpll_mode_get,
>> > +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> > +};
>> > +
>> > +static struct dpll_pin_ops dpll_pins_ops = {
>> 
>> const
>> 
>
>Yep.
>
>> 
>> > +	.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 dpll_pin_properties prop;
>> > 	struct devlink *devlink;
>> > +	char sma[4] = "SMA0";
>> 
>> Won't fit. Just use:
>> char *sma = "SMA0"
>> to be safe
>> 
>Got it.
>
>> 
>> > 	struct ptp_ocp *bp;
>> > -	int err;
>> > +	int err, i;
>> > +	u64 clkid;
>> > 
>> > 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> > 	if (!devlink) {
>> > @@ -4226,8 +4377,44 @@ 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 (!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;
>> > +
>> > +	prop.description = &sma[0];
>> > +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>> > +	prop.type = DPLL_PIN_TYPE_EXT;
>> > +	prop.any_freq_max = 10000000;
>> > +	prop.any_freq_min = 0;
>> > +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> > +
>> > +	for (i = 0; i < 4; i++) {
>> > +		sma[3] = 0x31 + i;
>> 
>> Ugh. Just use the static const array as I suggested in the dpll patch
>> reply. Helps you avoid this sort of "magic".
>> 
>well, yes. but at the same time Arkadiusz raised a good question about
>accessing driver's memory from the subsystem code - doesn't look clean.

It is completely clean and we do it everywhere in the kernel.
No problem what so ever.


>
>> 
>> > +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>> > +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
>> 
>> How it could be NULL?
>> 
>
>Allocation fail?

No? I mean, I don't see such possibility of return value of dpll_pin_get()
Do you see it or are you just guessing? :)


>
>> 
>> > +			bp->sma[i].dpll_pin = NULL;
>> 
>> This would not be needed if the error path iterates over
>> indexes which were successul.
>> 
>
>Yeah, I'll make it the same way it's done in ice.
>
>
>> 
>> > +			goto out_dpll;
>> > +		}
>> > +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
>> > +		if (err)
>> > +			goto out_dpll;
>> > +	}
>> > +
>> > +	return 0;
>> > +out_dpll:
>> > +	for (i = 0; i < 4; i++) {
>> > +		if (bp->sma[i].dpll_pin)
>> 
>> You don't do unregister of pin here.
>> 
>Yeah, actually error path and unload path should be re-implemented.
>
>> 
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +	}
>> > +	dpll_device_put(bp->dpll);
>> > out:
>> > 	ptp_ocp_detach(bp);
>> > out_disable:
>> > @@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> > 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> > 	struct devlink *devlink = priv_to_devlink(bp);
>> > 
>> > +	dpll_device_unregister(bp->dpll);
>> > +	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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-15 12:24         ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-15 12:24 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>On 14/03/2023 10:05, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>> > 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>> > 2 files changed, 200 insertions(+), 10 deletions(-)
>> > 
>> > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> > index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>> > --- a/drivers/ptp/ptp_ocp.c
>> > +++ b/drivers/ptp/ptp_ocp.c
>> > @@ -23,6 +23,8 @@
>> > #include <linux/mtd/mtd.h>
>> > #include <linux/nvmem-consumer.h>
>> > #include <linux/crc16.h>
>> > +#include <linux/dpll.h>
>> > +#include <uapi/linux/dpll.h>
>> 
>> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
>> 
>
>Ah, yeah, you've mentioned it for ice driver last time, but I forgot to check
>it here. Removed.
>
>> 
>> > 
>> > #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> > #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> > @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>> > 	bool	fixed_dir;
>> > 	bool	disabled;
>> > 	u8	default_fcn;
>> > +	struct dpll_pin *dpll_pin;
>> > };
>> > 
>> > struct ocp_attr_group {
>> > @@ -353,6 +356,7 @@ struct ptp_ocp {
>> > 	struct ptp_ocp_signal	signal[4];
>> > 	struct ptp_ocp_sma_connector sma[4];
>> 
>> It is quite customary to use defines for const numbers like this. Why
>> don't you do that in ptp_ocp?
>
>Historical reasons. Jonathan might answer this question.
>I will add it to my TODO list for the driver.
>
>> > 	const struct ocp_sma_op *sma_op;
>> > +	struct dpll_device *dpll;
>> > };
>> > 
>> > #define OCP_REQ_TIMESTAMP	BIT(0)
>> > @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> > }
>> > 
>> > static int
>> > -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> > {
>> > 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > -	enum ptp_ocp_sma_mode mode;
>> > -	int val;
>> > -
>> > -	mode = sma->mode;
>> > -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > -	if (val < 0)
>> > -		return val;
>> > 
>> > 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> > 		return -EOPNOTSUPP;
>> > @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > 	return val;
>> > }
>> > 
>> > +static int
>> > +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > +	enum ptp_ocp_sma_mode mode;
>> > +	int val;
>> > +
>> > +	mode = sma->mode;
>> > +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > +	if (val < 0)
>> > +		return val;
>> > +
>> > +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> > +}
>> > +
>> > static ssize_t
>> > sma1_store(struct device *dev, struct device_attribute *attr,
>> > 	   const char *buf, size_t count)
>> > @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> > 	device_unregister(&bp->dev);
>> > }
>> > 
>> > +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> > +{
>> > +	int i;
>> > +
>> > +	for (i = 0; i < 4; i++) {
>> > +		if (bp->sma[i].dpll_pin == pin)
>> > +			return i;
>> 
>> Just pass &bp->sma[i] as a priv to dpll_pin_register().
>> and get that pointer directly in pin ops. No need for lookup and use of
>> sma_nr at all.
>
>I'm still thinking about the change that you proposed to remove these
>_priv() helpers. I have to look into ice code to be sure we won't break
>any assumptions in it with moving to additional (void *) parameter.

There are basically 2 ways:
someop(struct subsystemobj *x, void *priv)
{
	struct *mine = priv;
}
or:
someop(struct subsystemobj *x)
{
	struct *mine = subsystem_get_priv(x);
}

Both are more or less equal. The first has benefit that the caller most
usually has direct access to the priv, so it can just easily pass it on.
Also, you as the driver write see right away there is a priv arg and
makes you want to use it and not figure out odd lookups to get to the
same priv.


>
>> 
>> > +	}
>> > +	return -1;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> > +				    enum dpll_lock_status *status,
>> > +				    struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> 
>> No need to cast (void *), remove it.
>> 
>Fixed everywhere in the code, thanks.
>
>
>> 
>> > +	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,
>> > +				    u32 *idx, struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +
>> > +	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,
>> > +				    u32 *mode, struct netlink_ext_ack *extack)
>> > +{
>> > +	*mode = DPLL_MODE_AUTOMATIC;
>> > +
>> 
>> No need for empty line, I believe.
>
>Ok.
>
>> > +	return 0;
>> > +}
>> > +
>> > +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> > +				    const enum dpll_mode mode,
>> > +				    struct netlink_ext_ack *extack)
>> > +{
>> > +	if (mode == DPLL_MODE_AUTOMATIC)
>> > +		return true;
>> > +
>> > +	return false;
>> 
>> Just:
>> 	return mode == DPLL_MODE_AUTOMATIC;
>> 
>Done, thanks!
>
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> > +				     const struct dpll_device *dpll,
>> > +				     enum dpll_pin_direction *direction,
>> > +				     struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	*direction = bp->sma[sma_nr].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,
>> > +				     const struct dpll_device *dpll,
>> > +				     enum dpll_pin_direction direction,
>> > +				     struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	enum ptp_ocp_sma_mode mode;
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	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,
>> > +			      const struct dpll_device *dpll,
>> > +			      const u32 frequency,
>> 
>> Why you need const for u32?
>
>No need, true, removed.
>
>> 
>> > +			      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	int val = frequency == 10000000 ? 0 : 1;
>> 
>> This is weird. I believe you should handle unsupported frequencies
>> gracefully.
>> 
>
>Currently hardware supports fixed frequencies only. That's why I have to
>do this kind of "dance". Hopefully we will improve it to support variable
>frequency.
>
>> 
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +
>> 
>> Avoid double empty lines.
>> 
>
>Yep.
>
>> 
>> > +	return ptp_ocp_sma_store_val(bp, val, bp->sma[sma_nr].mode, sma_nr);
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> > +			      const struct dpll_device *dpll,
>> > +			      u32 *frequency,
>> > +			      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>> > +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>> > +	u32 val;
>> > +
>> > +	if (sma_nr < 0)
>> > +		return -EINVAL;
>> > +
>> > +	val = bp->sma_op->get(bp, sma_nr);
>> > +	if (!val)
>> > +		*frequency = 1000000;
>> > +	else
>> > +		*frequency = 0;
>> 
>> I don't follow. How the frequency could be 0? Does that mean that the
>> pin is disabled? If yes, you should rather implement
>> .state_on_dpll_get
>> .state_on_dpll_set
>> 
>> and leave the frequency constant.
>
>It's actually a mistake. It should be 1 which means 1Hz, PPS. The value
>returned by sma_op->get is actually the type of the signal where 0 is 1PPS, 1
>is 10Mhz and so on.

So you support freq change? In the comment above you wrote "Currently
hardware supports fixed frequencies only". I'm confused.
The point is:
1) if you support freq change, you should sanitize the input properly in
   frequency_set() and return correct actual value in frequency_get()
2) if you don't support freq change, avoid filling-up these ops
   entirely.

>
>> 
>> 
>> > +	return 0;
>> > +}
>> > +
>> > +static struct dpll_device_ops dpll_ops = {
>> 
>> const
>> 
>
>Will change it together with API part. Otherwise it doesn't compile.
>
>> 
>> > +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> > +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
>> 
>> Should be called "source_pin_get" and return (struct dpll_pin *)
>> On the driver api level, no reason to pass indexes. Work with objects.
>> 
>
>Good point, will improve it.
>
>> 
>> > +	.mode_get = ptp_ocp_dpll_mode_get,
>> > +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> > +};
>> > +
>> > +static struct dpll_pin_ops dpll_pins_ops = {
>> 
>> const
>> 
>
>Yep.
>
>> 
>> > +	.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 dpll_pin_properties prop;
>> > 	struct devlink *devlink;
>> > +	char sma[4] = "SMA0";
>> 
>> Won't fit. Just use:
>> char *sma = "SMA0"
>> to be safe
>> 
>Got it.
>
>> 
>> > 	struct ptp_ocp *bp;
>> > -	int err;
>> > +	int err, i;
>> > +	u64 clkid;
>> > 
>> > 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> > 	if (!devlink) {
>> > @@ -4226,8 +4377,44 @@ 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 (!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;
>> > +
>> > +	prop.description = &sma[0];
>> > +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>> > +	prop.type = DPLL_PIN_TYPE_EXT;
>> > +	prop.any_freq_max = 10000000;
>> > +	prop.any_freq_min = 0;
>> > +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> > +
>> > +	for (i = 0; i < 4; i++) {
>> > +		sma[3] = 0x31 + i;
>> 
>> Ugh. Just use the static const array as I suggested in the dpll patch
>> reply. Helps you avoid this sort of "magic".
>> 
>well, yes. but at the same time Arkadiusz raised a good question about
>accessing driver's memory from the subsystem code - doesn't look clean.

It is completely clean and we do it everywhere in the kernel.
No problem what so ever.


>
>> 
>> > +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>> > +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
>> 
>> How it could be NULL?
>> 
>
>Allocation fail?

No? I mean, I don't see such possibility of return value of dpll_pin_get()
Do you see it or are you just guessing? :)


>
>> 
>> > +			bp->sma[i].dpll_pin = NULL;
>> 
>> This would not be needed if the error path iterates over
>> indexes which were successul.
>> 
>
>Yeah, I'll make it the same way it's done in ice.
>
>
>> 
>> > +			goto out_dpll;
>> > +		}
>> > +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp, NULL);
>> > +		if (err)
>> > +			goto out_dpll;
>> > +	}
>> > +
>> > +	return 0;
>> > +out_dpll:
>> > +	for (i = 0; i < 4; i++) {
>> > +		if (bp->sma[i].dpll_pin)
>> 
>> You don't do unregister of pin here.
>> 
>Yeah, actually error path and unload path should be re-implemented.
>
>> 
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +	}
>> > +	dpll_device_put(bp->dpll);
>> > out:
>> > 	ptp_ocp_detach(bp);
>> > out_disable:
>> > @@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> > 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> > 	struct devlink *devlink = priv_to_devlink(bp);
>> > 
>> > +	dpll_device_unregister(bp->dpll);
>> > +	dpll_device_put(bp->dpll);
>> > 	devlink_unregister(devlink);
>> > 	ptp_ocp_detach(bp);
>> > 	pci_disable_device(pdev);
>> > -- 
>> > 2.34.1
>> > 
>

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

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

Tue, Mar 14, 2023 at 07:35:55PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 4:45 PM
>>
>>[...]
>>
>>
>>>diff --git a/MAINTAINERS b/MAINTAINERS
>>>index edd3d562beee..0222b19af545 100644
>>>--- a/MAINTAINERS
>>>+++ b/MAINTAINERS
>>>@@ -6289,6 +6289,15 @@ 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
>>
>>Why "clock"? You don't mention "clock" anywhere else.
>>
>>[...]
>>
>>
>>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>>new file mode 100644
>>>index 000000000000..3fc151e16751
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_core.c
>>>@@ -0,0 +1,835 @@
>>>+// SPDX-License-Identifier: GPL-2.0
>>>+/*
>>>+ *  dpll_core.c - Generic DPLL Management class support.
>>
>>Why "class" ?
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>>>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>>>+{
>>>+	enum dpll_pin_freq_supp fs;
>>>+	struct dpll_pin_ref *ref;
>>>+	unsigned long i;
>>>+	u32 freq;
>>>+
>>>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>>>+		if (ref && ref->ops && ref->dpll)
>>>+			break;
>>>+	}
>>>+	if (!ref || !ref->ops || !ref->dpll)
>>>+		return -ENODEV;
>>>+	if (!ref->ops->frequency_get)
>>>+		return -EOPNOTSUPP;
>>>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>>>+		return -EFAULT;
>>>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>>>+		return -EMSGSIZE;
>>>+	if (!dump_any_freq)
>>>+		return 0;
>>>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>>>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>>>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>>>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>>+			    dpll_pin_freq_value[fs]))
>>
>>This is odd. As I suggested in the yaml patch, better to treat all
>>supported frequencies the same, no matter if it is range or not. The you
>>don't need this weird bitfield.
>>
>>You can have a macro to help driver to assemble array of supported
>>frequencies and ranges.
>>
>
>I understand suggestion on yaml, but here I am confused.
>How do they relate to the supported frequency passed between driver and dpll
>subsystem?
>This bitfield is not visible to the userspace, and sure probably adding macro
>can be useful.

My point is to avoid the bitfield and to treat supported frequencies and
ranges in the same way. It can look similar to this:

in dpll.h:

struct struct dpll_pin_frequency {
	u64 min;
	u64 max;
};

#define DPLL_PIN_FREQUENCY_RANGE(_min, _mac)	\
	{					\
		.min = _min,			\
		.max = _max,			\
	}

#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
#define DPLL_PIN_FREQUENCY_1PPS DPLL_PIN_FREQUENCY(1)
#define DPLL_PIN_FREQUENCY_10MHZ DPLL_PIN_FREQUENCY(1000000)


Then in driver you have:

static const struct dpll_pin_frequency pcp_dpll_pin_freqs[] = {
	DPLL_PIN_FREQUENCY_1PPS,
	DPLL_PIN_FREQUENCY_10MHZ,
	DPLL_PIN_FREQUENCY(4000000),
	DPLL_PIN_FREQUENCY_RANGE(500, 1000),
	DPLL_PIN_FREQUENCY_RANGE(9000, 10000),
};

static const struct dpll_pin_properties pcp_dpll_pin_props = {
	.label = "SMA",
	.frequencies_supported = pcp_dpll_pin_freqs,
	.frequencies_supported_count = ARRAY_SIZE(pcp_dpll_pin_freqs),
	.type = DPLL_PIN_TYPE_EXT,
	.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
};


Then the dpll core could very easily iterate over .frequencies_supported
array and dump the supported values and ranges to user in uniform way.


>
>>
>>>+				return -EMSGSIZE;
>>>+		}
>>>+	}
>>>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>>+				pin->prop.any_freq_min))
>>>+			return -EMSGSIZE;
>>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>>+				pin->prop.any_freq_max))
>>>+			return -EMSGSIZE;
>>>+	}
>>>+
>>>+	return 0;
>>>+}
>>>+
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>>+			 struct dpll_device *dpll,
>>>+			 struct netlink_ext_ack *extack)
>>>+{
>>>+	struct dpll_pin_ref *ref;
>>>+	int ret;
>>>+
>>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>+		return -EMSGSIZE;
>>>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>>>+		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, extack);
>>>+	if (ret)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>+	if (!ref)
>>
>>How exactly this can happen? Looks to me like only in case of a bug.
>>WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?
>
>Yes, makes sense.
>
>>
>>
>>>+		return -EFAULT;
>>>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>>>+	if (ret)
>>>+		return ret;
>>>+	if (pin->rclk_dev_name)
>>
>>Use && and single if
>>
>
>Make sense to me.
>
>>
>>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>>+				   pin->rclk_dev_name))
>>>+			return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>>>+		  struct netlink_ext_ack *extack)
>>>+{
>>>+	u32 freq = nla_get_u32(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) {
>>>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>>>+		if (ret)
>>>+			return -EFAULT;
>>
>>return what the op returns: ret
>
>Why would we return here a driver return code, userspace can have this info
>from extack. IMHO return values of dpll subsystem shall be not dependent on
>what is returned from the driver.

Why not to return it? The driver had some problem, errno suggests what
that was. It is completely desired to pass that along and actually,
it's been done like this in the rest of the netlink ops I can think of.
Why would you want to hide it? Extack carries string message,
not related to this directly.


>
>>
>>
>>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>>>+	}
>>>+
>>>+	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) {
>>>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>>
>>ret = ..
>>if (ret)
>>	return ret;
>>
>>Please use this pattern in other ops call code as well.
>>
>
>This is the same as above (return code by driver) explanation.

Same reply as above.


>
>>
>>>+			return -EFAULT;
>>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>>>+	}
>>>+
>>>+	return 0;
>>
>>[...]
>
>Thanks,
>Arkadiusz

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

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

Tue, Mar 14, 2023 at 07:35:55PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 4:45 PM
>>
>>[...]
>>
>>
>>>diff --git a/MAINTAINERS b/MAINTAINERS
>>>index edd3d562beee..0222b19af545 100644
>>>--- a/MAINTAINERS
>>>+++ b/MAINTAINERS
>>>@@ -6289,6 +6289,15 @@ 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
>>
>>Why "clock"? You don't mention "clock" anywhere else.
>>
>>[...]
>>
>>
>>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>>new file mode 100644
>>>index 000000000000..3fc151e16751
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_core.c
>>>@@ -0,0 +1,835 @@
>>>+// SPDX-License-Identifier: GPL-2.0
>>>+/*
>>>+ *  dpll_core.c - Generic DPLL Management class support.
>>
>>Why "class" ?
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
>>>+		      struct netlink_ext_ack *extack, bool dump_any_freq)
>>>+{
>>>+	enum dpll_pin_freq_supp fs;
>>>+	struct dpll_pin_ref *ref;
>>>+	unsigned long i;
>>>+	u32 freq;
>>>+
>>>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>>>+		if (ref && ref->ops && ref->dpll)
>>>+			break;
>>>+	}
>>>+	if (!ref || !ref->ops || !ref->dpll)
>>>+		return -ENODEV;
>>>+	if (!ref->ops->frequency_get)
>>>+		return -EOPNOTSUPP;
>>>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>>>+		return -EFAULT;
>>>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>>>+		return -EMSGSIZE;
>>>+	if (!dump_any_freq)
>>>+		return 0;
>>>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>>>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>>>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>>>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>>+			    dpll_pin_freq_value[fs]))
>>
>>This is odd. As I suggested in the yaml patch, better to treat all
>>supported frequencies the same, no matter if it is range or not. The you
>>don't need this weird bitfield.
>>
>>You can have a macro to help driver to assemble array of supported
>>frequencies and ranges.
>>
>
>I understand suggestion on yaml, but here I am confused.
>How do they relate to the supported frequency passed between driver and dpll
>subsystem?
>This bitfield is not visible to the userspace, and sure probably adding macro
>can be useful.

My point is to avoid the bitfield and to treat supported frequencies and
ranges in the same way. It can look similar to this:

in dpll.h:

struct struct dpll_pin_frequency {
	u64 min;
	u64 max;
};

#define DPLL_PIN_FREQUENCY_RANGE(_min, _mac)	\
	{					\
		.min = _min,			\
		.max = _max,			\
	}

#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
#define DPLL_PIN_FREQUENCY_1PPS DPLL_PIN_FREQUENCY(1)
#define DPLL_PIN_FREQUENCY_10MHZ DPLL_PIN_FREQUENCY(1000000)


Then in driver you have:

static const struct dpll_pin_frequency pcp_dpll_pin_freqs[] = {
	DPLL_PIN_FREQUENCY_1PPS,
	DPLL_PIN_FREQUENCY_10MHZ,
	DPLL_PIN_FREQUENCY(4000000),
	DPLL_PIN_FREQUENCY_RANGE(500, 1000),
	DPLL_PIN_FREQUENCY_RANGE(9000, 10000),
};

static const struct dpll_pin_properties pcp_dpll_pin_props = {
	.label = "SMA",
	.frequencies_supported = pcp_dpll_pin_freqs,
	.frequencies_supported_count = ARRAY_SIZE(pcp_dpll_pin_freqs),
	.type = DPLL_PIN_TYPE_EXT,
	.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
};


Then the dpll core could very easily iterate over .frequencies_supported
array and dump the supported values and ranges to user in uniform way.


>
>>
>>>+				return -EMSGSIZE;
>>>+		}
>>>+	}
>>>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>>+				pin->prop.any_freq_min))
>>>+			return -EMSGSIZE;
>>>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>>+				pin->prop.any_freq_max))
>>>+			return -EMSGSIZE;
>>>+	}
>>>+
>>>+	return 0;
>>>+}
>>>+
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>>>+			 struct dpll_device *dpll,
>>>+			 struct netlink_ext_ack *extack)
>>>+{
>>>+	struct dpll_pin_ref *ref;
>>>+	int ret;
>>>+
>>>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>+		return -EMSGSIZE;
>>>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>>>+		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, extack);
>>>+	if (ret)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>+	if (!ref)
>>
>>How exactly this can happen? Looks to me like only in case of a bug.
>>WARN_ON() perhaps (put directly into dpll_xa_ref_dpll_find()?
>
>Yes, makes sense.
>
>>
>>
>>>+		return -EFAULT;
>>>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>>>+	if (ret && ret != -EOPNOTSUPP)
>>>+		return ret;
>>>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>>>+	if (ret)
>>>+		return ret;
>>>+	if (pin->rclk_dev_name)
>>
>>Use && and single if
>>
>
>Make sense to me.
>
>>
>>>+		if (nla_put_string(msg, DPLL_A_PIN_RCLK_DEVICE,
>>>+				   pin->rclk_dev_name))
>>>+			return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>
>>[...]
>>
>>
>>>+static int
>>>+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
>>>+		  struct netlink_ext_ack *extack)
>>>+{
>>>+	u32 freq = nla_get_u32(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) {
>>>+		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
>>>+		if (ret)
>>>+			return -EFAULT;
>>
>>return what the op returns: ret
>
>Why would we return here a driver return code, userspace can have this info
>from extack. IMHO return values of dpll subsystem shall be not dependent on
>what is returned from the driver.

Why not to return it? The driver had some problem, errno suggests what
that was. It is completely desired to pass that along and actually,
it's been done like this in the rest of the netlink ops I can think of.
Why would you want to hide it? Extack carries string message,
not related to this directly.


>
>>
>>
>>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
>>>+	}
>>>+
>>>+	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) {
>>>+		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
>>
>>ret = ..
>>if (ret)
>>	return ret;
>>
>>Please use this pattern in other ops call code as well.
>>
>
>This is the same as above (return code by driver) explanation.

Same reply as above.


>
>>
>>>+			return -EFAULT;
>>>+		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
>>>+	}
>>>+
>>>+	return 0;
>>
>>[...]
>
>Thanks,
>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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]

>+struct dpll_device
>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);

[...]

>+struct dpll_pin
>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>+	      const struct dpll_pin_properties *pin_prop);

Small tweak, please use the same trick as shown for example here:
/* pci_register_driver() must be a macro so KBUILD_MODNAME can be expanded */
#define pci_register_driver(driver)             \
        __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

The driver calls header helper which fills up the "THIS_MODULE" for it.

Thanks!

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]

>+struct dpll_device
>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);

[...]

>+struct dpll_pin
>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>+	      const struct dpll_pin_properties *pin_prop);

Small tweak, please use the same trick as shown for example here:
/* pci_register_driver() must be a macro so KBUILD_MODNAME can be expanded */
#define pci_register_driver(driver)             \
        __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

The driver calls header helper which fills up the "THIS_MODULE" for it.

Thanks!

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-15 15:34     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-15 15:34 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]

>@@ -4226,8 +4377,44 @@ 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 (!bp->dpll) {

You have to use IS_ERR() here. Same problem in ice.

[...]

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-15 15:34     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-15 15:34 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]

>@@ -4226,8 +4377,44 @@ 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 (!bp->dpll) {

You have to use IS_ERR() here. Same problem in ice.

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-15 15:34     ` Jiri Pirko
@ 2023-03-15 15:52       ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-15 15:52 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 15/03/2023 15:34, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
> 
> [...]
> 
>> @@ -4226,8 +4377,44 @@ 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 (!bp->dpll) {
> 
> You have to use IS_ERR() here. Same problem in ice.
> 

Yep, fixed, thanks!


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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-15 15:52       ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-15 15:52 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 15/03/2023 15:34, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
> 
> [...]
> 
>> @@ -4226,8 +4377,44 @@ 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 (!bp->dpll) {
> 
> You have to use IS_ERR() here. Same problem in ice.
> 

Yep, fixed, thanks!


_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-16 12:12     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 12:12 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]


>@@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
> 	struct devlink *devlink = priv_to_devlink(bp);
> 

You are missing pins unregister and put here.

Btw, It would be probably good to add a WARN_ON into
dpll_device_unregister() to catch bugs like this one.



>+	dpll_device_unregister(bp->dpll);
>+	dpll_device_put(bp->dpll);
> 	devlink_unregister(devlink);
> 	ptp_ocp_detach(bp);
> 	pci_disable_device(pdev);
>-- 
>2.34.1
>

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-16 12:12     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 12:12 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]


>@@ -4243,6 +4430,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
> 	struct devlink *devlink = priv_to_devlink(bp);
> 

You are missing pins unregister and put here.

Btw, It would be probably good to add a WARN_ON into
dpll_device_unregister() to catch bugs like this one.



>+	dpll_device_unregister(bp->dpll);
>+	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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+/**
>+ * dpll_pin_unregister - deregister dpll pin from dpll device
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * Note: It does not free the memory
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)

Make this return void. Function does not report anything useful,
non-0 is only in case of WARN_ON. Nobody is going to check that ever
anyway.


>+{
>+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>+		return -ENOENT;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	__dpll_pin_unregister(dpll, pin);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_unregister);

[...]


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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+/**
>+ * dpll_pin_unregister - deregister dpll pin from dpll device
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * Note: It does not free the memory
>+ */
>+int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)

Make this return void. Function does not report anything useful,
non-0 is only in case of WARN_ON. Nobody is going to check that ever
anyway.


>+{
>+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
>+		return -ENOENT;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	mutex_lock(&dpll_pin_xa_lock);
>+	__dpll_pin_unregister(dpll, pin);
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return 0;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_unregister);

[...]


_______________________________________________
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] 202+ messages in thread

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

Wed, Mar 15, 2023 at 10:22:33AM CET, jiri@resnulli.us wrote:
>Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, March 14, 2023 10:22 AM
>>>
>>>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>>>>> > +	     const struct dpll_pin_properties *prop)
>>>>> > +{
>>>>> > +	struct dpll_pin *pos, *ret = NULL;
>>>>> > +	unsigned long index;
>>>>> > +
>>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>>> > +		if (pos->clock_id == clock_id &&
>>>>> > +		    pos->dev_driver_id == device_drv_id &&
>>>>> > +		    pos->module == module) {
>>>>>
>>>>> Compare prop as well.
>>>>>
>>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>>> should. And therefore it could be easily put inside.
>>>>>
>>>>
>>>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>>>thoughts?
>>>
>>>IDK, no strong opinion on this. I just thought it may help to identify
>>>the pin and avoid potential driver bugs. (Like registering 2 pins with

Was thinking about this some more, I think that it would be good to
store and check properties in case of pin_get() call. If the driver does
call for the second time pin_get() for the same pin (clockid,pin_index)
with different prop, it is buggy and WARN_ON should be triggered.
WARN_ON check should compare the actual struct, not just pointer.

[...]

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

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

Wed, Mar 15, 2023 at 10:22:33AM CET, jiri@resnulli.us wrote:
>Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, March 14, 2023 10:22 AM
>>>
>>>Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>>On 13.03.2023 16:21, Jiri Pirko wrote:
>>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>>>>> > +	     const struct dpll_pin_properties *prop)
>>>>> > +{
>>>>> > +	struct dpll_pin *pos, *ret = NULL;
>>>>> > +	unsigned long index;
>>>>> > +
>>>>> > +	mutex_lock(&dpll_pin_xa_lock);
>>>>> > +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>>> > +		if (pos->clock_id == clock_id &&
>>>>> > +		    pos->dev_driver_id == device_drv_id &&
>>>>> > +		    pos->module == module) {
>>>>>
>>>>> Compare prop as well.
>>>>>
>>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>>> should. And therefore it could be easily put inside.
>>>>>
>>>>
>>>>I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>>And now I think that *prop is not needed here at all. Arkadiusz, any
>>>>thoughts?
>>>
>>>IDK, no strong opinion on this. I just thought it may help to identify
>>>the pin and avoid potential driver bugs. (Like registering 2 pins with

Was thinking about this some more, I think that it would be good to
store and check properties in case of pin_get() call. If the driver does
call for the second time pin_get() for the same pin (clockid,pin_index)
with different prop, it is buggy and WARN_ON should be triggered.
WARN_ON check should compare the actual struct, not just pointer.

[...]

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	pin->dev_driver_id = device_drv_id;
>+	pin->clock_id = clock_id;
>+	pin->module = module;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->description)) {

Why is this mandatory? I'm now working on mlx5 implementation, don't
really know what to put here for SyncE. The type of the pin SyncE tells
all I need. I would like to avoid description fill-up.


>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>+	if (!pin->prop.description) {
>+		ret = -ENOMEM;
>+		goto release;
>+	}

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u8 device_drv_id,	struct module *module,
>+	       const struct dpll_pin_properties *prop)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	pin->dev_driver_id = device_drv_id;
>+	pin->clock_id = clock_id;
>+	pin->module = module;
>+	refcount_set(&pin->refcount, 1);
>+	if (WARN_ON(!prop->description)) {

Why is this mandatory? I'm now working on mlx5 implementation, don't
really know what to put here for SyncE. The type of the pin SyncE tells
all I need. I would like to avoid description fill-up.


>+		ret = -EINVAL;
>+		goto release;
>+	}
>+	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>+	if (!pin->prop.description) {
>+		ret = -ENOMEM;
>+		goto release;
>+	}

[...]

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-14 14:44     ` Jiri Pirko
@ 2023-03-16 13:15       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-16 13:15 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 3:44 PM
>
>Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
>> drivers/dpll/dpll_nl.c                | 126 +++++++
>> drivers/dpll/dpll_nl.h                |  42 +++
>> include/uapi/linux/dpll.h             | 196 ++++++++++
>> 4 files changed, 878 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..03e9f0e2d3d2
>>--- /dev/null
>>+++ b/Documentation/netlink/specs/dpll.yaml
>>@@ -0,0 +1,514 @@
>>+name: dpll
>>+
>>+doc: DPLL subsystem.
>>+
>>+definitions:
>>+  -
>>+    type: const
>>+    name: temp-divider
>>+    value: 10
>>+  -
>>+    type: const
>>+    name: pin-freq-1-hz
>>+    value: 1
>>+  -
>>+    type: const
>>+    name: pin-freq-10-mhz
>>+    value: 10000000
>>+  -
>>+    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 (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: enum
>>+    name: pin-type
>>+    doc: Enumerates 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-state
>>+    doc: available pin modes
>>+    entries:
>>+      -
>>+        name: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: connected
>>+        doc: pin connected
>>+      -
>>+        name: disconnected
>>+        doc: pin disconnected
>>+    render-max: true
>>+  -
>>+    type: enum
>>+    name: pin-direction
>>+    doc: available pin direction
>>+    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: enum
>>+    name: mode
>
>Could you please sort the enums so they are in the same order as the
>attribute which carries them? That would also put the device and pin
>enums together.
>

Fixed.

>
>>+    doc: |
>>+      working-modes a dpll can support, differentiate if and how dpll
>>selects
>>+      one of its sources to syntonize with it
>>+    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: 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: 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
>>+  -
>>+    type: flags
>>+    name: pin-caps
>>+    doc: define capabilities of a pin
>>+    entries:
>>+      -
>>+        name: direction-can-change
>>+      -
>>+        name: priority-can-change
>>+      -
>>+        name: state-can-change
>>+
>>+
>>+attribute-sets:
>>+  -
>>+    name: dpll
>>+    enum-name: dplla
>>+    attributes:
>>+      -
>>+        name: device
>>+        type: nest
>>+        value: 1
>>+        multi-attr: true
>>+        nested-attributes: device
>
>What is this "device" and what is it good for? Smells like some leftover
>and with the nested scheme looks quite odd.
>

No, it is nested attribute type, used when multiple devices are returned with
netlink:

- dump of device-get command where all devices are returned, each one nested
inside it:
[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]

- do/dump of pin-get, in case of shared pins, each pin contains number of dpll
handles it connects with:
[{'pin': [{'device': [{'bus-name': 'pci',
                       'dev-name': '0000:21:00.0_0',
                       'id': 0,
                       'pin-prio': 6,
                       'pin-state': {'doc': 'pin connected',
                                     'name': 'connected'}},
                      {'bus-name': 'pci',
                       'dev-name': '0000:21:00.0_1',
                       'id': 1,
                       'pin-prio': 8,
                       'pin-state': {'doc': 'pin connected',
                                     'name': 'connected'}}],
           'pin-direction': {'doc': 'pin used as a source of a signal',
                             'name': 'source'},
           'pin-frequency': 1,
           'pin-frequency-supported': [1, 10000000],
           'pin-idx': 0,
	...

>
>>+      -
>>+        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: source-pin-idx
>>+        type: u32
>>+      -
>>+        name: lock-status
>>+        type: u8
>>+        enum: lock-status
>>+      -
>>+        name: temp
>
>Could you put some comment regarding the divider here?
>

Sure, fixed.

>
>>+        type: s32
>>+      -
>>+        name: clock-id
>>+        type: u64
>>+      -
>>+        name: type
>>+        type: u8
>>+        enum: type
>>+      -
>>+        name: pin
>>+        type: nest
>>+        multi-attr: true
>>+        nested-attributes: pin
>>+        value: 12
>>+      -
>>+        name: pin-idx
>>+        type: u32
>>+      -
>>+        name: pin-description
>>+        type: string
>>+      -
>>+        name: pin-type
>>+        type: u8
>>+        enum: pin-type
>>+      -
>>+        name: pin-direction
>>+        type: u8
>>+        enum: pin-direction
>>+      -
>>+        name: pin-frequency
>>+        type: u32
>>+      -
>>+        name: pin-frequency-supported
>>+        type: u32
>>+        multi-attr: true
>>+      -
>>+        name: pin-any-frequency-min
>>+        type: u32
>>+      -
>>+        name: pin-any-frequency-max
>>+        type: u32
>
>These 2 attrs somehow overlap with pin-frequency-supported,
>which is a bit confusing, I think that pin-frequency-supported
>should carry all supported frequencies. How about to have something
>like:
>        name: pin-frequency-supported
>        type: nest
>	multi-attr: true
>        nested-attributes: pin-frequency-range
>
>and then:
>      name: pin-frequency-range
>      subset-of: dpll
>      attributes:
>        -
>          name: pin-frequency-min
>          type: u32
>        -
>          name: pin-frequency-max
>          type: u32
>          doc: should be put only to specify range when value differs
>	       from pin-frequency-min
>
>That could carry list of frequencies and ranges, in this case multiple
>ones if possibly needed.
>

Sure

>
>
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent
>>+        type: nest
>>+        multi-attr: true
>>+        nested-attributes: pin
>>+        value: 23
>
>Value 23? What's this?
>You have it specified for some attrs all over the place.
>What is the reason for it?
>

Actually this particular one is not needed (also value: 12 on pin above),
I will remove those.
But the others you are refering to (the ones in nested attribute list),
are required because of cli.py parser issue, maybe Kuba knows a better way to
prevent the issue?
Basically, without those values, cli.py brakes on parsing responses, after
every "jump" to nested attribute list it is assigning first attribute there
with value=0, thus there is a need to assign a proper value, same as it is on
'main' attribute list.

>
>>+      -
>>+        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: source-pin-idx
>>+        type: u32
>>+      -
>>+        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
>>+        type: nest
>>+        value: 12
>>+        multi-attr: true
>>+        nested-attributes: pin
>
>This does not belong here.
>

What do you mean?
With device-get 'do' request the list of pins connected to the dpll is
returned, each pin is nested in this attribute.
This is required by parser to work.

>
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+        value: 21
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-dpll-caps
>>+        type: u32
>>+        value: 26
>
>All these 3 do not belong here are well.
>

Same as above explanation.

>
>
>>+  -
>>+    name: pin
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: device
>>+        type: nest
>>+        value: 1
>>+        multi-attr: true
>>+        nested-attributes: device
>>+      -
>>+        name: pin-idx
>>+        type: u32
>>+        value: 13
>>+      -
>>+        name: pin-description
>>+        type: string
>>+      -
>>+        name: pin-type
>>+        type: u8
>>+        enum: pin-type
>>+      -
>>+        name: pin-direction
>>+        type: u8
>>+        enum: pin-direction
>>+      -
>>+        name: pin-frequency
>>+        type: u32
>>+      -
>>+        name: pin-frequency-supported
>>+        type: u32
>>+        multi-attr: true
>>+      -
>>+        name: pin-any-frequency-min
>>+        type: u32
>>+      -
>>+        name: pin-any-frequency-max
>>+        type: u32
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent
>>+        type: nest
>>+        multi-attr: true
>
>Multiple parents? How is that supposed to work?
>

As we have agreed, MUXed pins can have multiple parents.
In our case:
/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-get --json '{"id": 0, "pin-idx":13}'
{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
                     {'bus-name': 'pci',
                      'dev-name': '0000:21:00.0_1',
                      'id': 1}],
          'pin-description': '0000:21:00.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'}}],
          'pin-rclk-device': '0000:21:00.0',
          'pin-type': {'doc': "ethernet port PHY's recovered clock",
                       'name': 'synce-eth-port'}}]}


>
>>+        nested-attributes: pin-parent
>>+        value: 23
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>>+        value: 25
>>+      -
>>+        name: pin-dpll-caps
>>+        type: u32
>
>Missing "enum: "
>

It is actually a bitmask, this is why didn't set as enum, with enum type
parser won't parse it.

>
>>+  -
>>+    name: pin-parent
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        value: 22
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent-idx
>>+        type: u32
>>+        value: 24
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>
>Yeah, as I wrote in the other email, this really smells to
>have like a simple string like this. What is it supposed to be?
>

Yes, let's discuss there.

>
>>+
>>+
>>+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
>
>Shouldn't this be "device"?
>

It would brake the parser, again I hope Jakub Kicinski could take a look on
this.

>
>>+      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
>
>"device" here as well?
>

Same as above.

>
>>+      flags: [ admin-perm ]
>>+
>>+      do:
>>+        pre: dpll-pre-doit
>>+        post: dpll-post-doit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+            - mode
>
>Hmm, shouldn't source-pin-index be here as well?

No, there is no set for this.
For manual mode user selects the pin by setting enabled state on the one
he needs to recover signal from.

source-pin-index is read only, returns active source.

>
>>+
>>+    -
>>+      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
>
>"pin"?
>

Same case as with dpll/device above.

>
>>+      flags: [ admin-perm ]
>>+
>>+      do:
>>+        pre: dpll-pin-pre-doit
>>+        post: dpll-pin-post-doit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+            - pin-idx
>>+        reply:
>>+          attributes:
>>+            - pin
>>+
>>+      dump:
>>+        pre: dpll-pin-pre-dumpit
>>+        post: dpll-pin-post-dumpit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+        reply:
>>+          attributes:
>>+            - pin
>>+
>>+    -
>>+      name: pin-set
>>+      doc: Set attributes of a target pin
>>+      attribute-set: dpll
>
>"pin"?
>

Same case as with dpll/device above.

>
>>+      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-parent-idx
>>+            - pin-state
>>+
>>+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..099d1e30ca7c
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.c
>>@@ -0,0 +1,126 @@
>>+// SPDX-License-Identifier: 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),
>
>Hmm, any idea why the generator does not put define name
>here instead of "5"?
>

Not really, it probably needs a fix for this.

>
>>+};
>>+
>>+/* 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_U32, },
>
>4.3GHz would be the limit, isn't it future proof to make this rather u64?
>

Sure, fixed.

>
>>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
>
>This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
>is last. I found that the order is not according to the enum in the yaml
>operation definition. Perhaps the generator can sort this?
>

Fixed. This related to the order on list of attributes expected on ops
definition.

>
>>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>>+};
>>+
>>+/* Ops table for dpll */
>>+static const struct genl_split_ops dpll_nl_ops[6] = {
>>+	{
>>+		.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..3a354dfb9a31
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.h
>>@@ -0,0 +1,42 @@
>>+/* SPDX-License-Identifier: 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..ece6fe685d08
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,196 @@
>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>+/* 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
>>+
>>+#define DPLL_TEMP_DIVIDER	10
>
>Some comment to this define would be nice.
>

I added to the spec, but it is not generated by the regen script.

>
>>+#define DPLL_PIN_FREQ_1_HZ	1
>>+#define DPLL_PIN_FREQ_10_MHZ	10000000
>
>Have this as enum. Spell out "frequency" to be consistent with the
>related attribute name.
>

Sure, fixed.

>
>>+
>>+/**
>>+ * 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 (or
>is in
>
>"any valid source" perhaps?
>

Yes, fixed.

>
>>+ *   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)
>>+};
>>+
>>+/**
>>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
>
>For some of the enums, you say for what attribute it serves as a value
>set, for some you don't. Please unify the approach. I think it is
>valuable to say that for every enum.
>

Sure, fixed.

>
>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>>+ */
>>+enum dpll_pin_state {
>>+	DPLL_PIN_STATE_UNSPEC,
>>+	DPLL_PIN_STATE_CONNECTED,
>>+	DPLL_PIN_STATE_DISCONNECTED,
>>+
>>+	__DPLL_PIN_STATE_MAX,
>>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>>+};
>>+
>>+/**
>>+ * enum dpll_pin_direction - available pin direction
>>+ * @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)
>>+};
>>+
>>+/**
>>+ * enum dpll_mode - working-modes a dpll can support, differentiate if
>>and how
>>+ *   dpll selects one of its sources to syntonize with it
>>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>>+ */
>>+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 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_SOURCE_PIN_IDX,
>>+	DPLL_A_LOCK_STATUS,
>>+	DPLL_A_TEMP,
>>+	DPLL_A_CLOCK_ID,
>>+	DPLL_A_TYPE,
>>+	DPLL_A_PIN,
>>+	DPLL_A_PIN_IDX,
>>+	DPLL_A_PIN_DESCRIPTION,
>
>I commented this name in the other email. This does not describe
>anything. It is a label on a connector, isn't it? Why don't to call it
>"label"?
>

Sure, fixed.

>
>>+	DPLL_A_PIN_TYPE,
>>+	DPLL_A_PIN_DIRECTION,
>>+	DPLL_A_PIN_FREQUENCY,
>>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,
>
>Drop "any". Not good for anything.
>

Sure, fixed.

>
>>+	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,
>
>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>enum name.

Sure, fixed.

>
>>+
>>+	__DPLL_A_MAX,
>>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>>+};
>>+
>>+enum {
>>+	DPLL_CMD_UNSPEC,
>>+	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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-16 13:15       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-16 13:15 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 3:44 PM
>
>Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
>> drivers/dpll/dpll_nl.c                | 126 +++++++
>> drivers/dpll/dpll_nl.h                |  42 +++
>> include/uapi/linux/dpll.h             | 196 ++++++++++
>> 4 files changed, 878 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..03e9f0e2d3d2
>>--- /dev/null
>>+++ b/Documentation/netlink/specs/dpll.yaml
>>@@ -0,0 +1,514 @@
>>+name: dpll
>>+
>>+doc: DPLL subsystem.
>>+
>>+definitions:
>>+  -
>>+    type: const
>>+    name: temp-divider
>>+    value: 10
>>+  -
>>+    type: const
>>+    name: pin-freq-1-hz
>>+    value: 1
>>+  -
>>+    type: const
>>+    name: pin-freq-10-mhz
>>+    value: 10000000
>>+  -
>>+    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 (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: enum
>>+    name: pin-type
>>+    doc: Enumerates 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-state
>>+    doc: available pin modes
>>+    entries:
>>+      -
>>+        name: unspec
>>+        doc: unspecified value
>>+      -
>>+        name: connected
>>+        doc: pin connected
>>+      -
>>+        name: disconnected
>>+        doc: pin disconnected
>>+    render-max: true
>>+  -
>>+    type: enum
>>+    name: pin-direction
>>+    doc: available pin direction
>>+    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: enum
>>+    name: mode
>
>Could you please sort the enums so they are in the same order as the
>attribute which carries them? That would also put the device and pin
>enums together.
>

Fixed.

>
>>+    doc: |
>>+      working-modes a dpll can support, differentiate if and how dpll
>>selects
>>+      one of its sources to syntonize with it
>>+    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: 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: 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
>>+  -
>>+    type: flags
>>+    name: pin-caps
>>+    doc: define capabilities of a pin
>>+    entries:
>>+      -
>>+        name: direction-can-change
>>+      -
>>+        name: priority-can-change
>>+      -
>>+        name: state-can-change
>>+
>>+
>>+attribute-sets:
>>+  -
>>+    name: dpll
>>+    enum-name: dplla
>>+    attributes:
>>+      -
>>+        name: device
>>+        type: nest
>>+        value: 1
>>+        multi-attr: true
>>+        nested-attributes: device
>
>What is this "device" and what is it good for? Smells like some leftover
>and with the nested scheme looks quite odd.
>

No, it is nested attribute type, used when multiple devices are returned with
netlink:

- dump of device-get command where all devices are returned, each one nested
inside it:
[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]

- do/dump of pin-get, in case of shared pins, each pin contains number of dpll
handles it connects with:
[{'pin': [{'device': [{'bus-name': 'pci',
                       'dev-name': '0000:21:00.0_0',
                       'id': 0,
                       'pin-prio': 6,
                       'pin-state': {'doc': 'pin connected',
                                     'name': 'connected'}},
                      {'bus-name': 'pci',
                       'dev-name': '0000:21:00.0_1',
                       'id': 1,
                       'pin-prio': 8,
                       'pin-state': {'doc': 'pin connected',
                                     'name': 'connected'}}],
           'pin-direction': {'doc': 'pin used as a source of a signal',
                             'name': 'source'},
           'pin-frequency': 1,
           'pin-frequency-supported': [1, 10000000],
           'pin-idx': 0,
	...

>
>>+      -
>>+        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: source-pin-idx
>>+        type: u32
>>+      -
>>+        name: lock-status
>>+        type: u8
>>+        enum: lock-status
>>+      -
>>+        name: temp
>
>Could you put some comment regarding the divider here?
>

Sure, fixed.

>
>>+        type: s32
>>+      -
>>+        name: clock-id
>>+        type: u64
>>+      -
>>+        name: type
>>+        type: u8
>>+        enum: type
>>+      -
>>+        name: pin
>>+        type: nest
>>+        multi-attr: true
>>+        nested-attributes: pin
>>+        value: 12
>>+      -
>>+        name: pin-idx
>>+        type: u32
>>+      -
>>+        name: pin-description
>>+        type: string
>>+      -
>>+        name: pin-type
>>+        type: u8
>>+        enum: pin-type
>>+      -
>>+        name: pin-direction
>>+        type: u8
>>+        enum: pin-direction
>>+      -
>>+        name: pin-frequency
>>+        type: u32
>>+      -
>>+        name: pin-frequency-supported
>>+        type: u32
>>+        multi-attr: true
>>+      -
>>+        name: pin-any-frequency-min
>>+        type: u32
>>+      -
>>+        name: pin-any-frequency-max
>>+        type: u32
>
>These 2 attrs somehow overlap with pin-frequency-supported,
>which is a bit confusing, I think that pin-frequency-supported
>should carry all supported frequencies. How about to have something
>like:
>        name: pin-frequency-supported
>        type: nest
>	multi-attr: true
>        nested-attributes: pin-frequency-range
>
>and then:
>      name: pin-frequency-range
>      subset-of: dpll
>      attributes:
>        -
>          name: pin-frequency-min
>          type: u32
>        -
>          name: pin-frequency-max
>          type: u32
>          doc: should be put only to specify range when value differs
>	       from pin-frequency-min
>
>That could carry list of frequencies and ranges, in this case multiple
>ones if possibly needed.
>

Sure

>
>
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent
>>+        type: nest
>>+        multi-attr: true
>>+        nested-attributes: pin
>>+        value: 23
>
>Value 23? What's this?
>You have it specified for some attrs all over the place.
>What is the reason for it?
>

Actually this particular one is not needed (also value: 12 on pin above),
I will remove those.
But the others you are refering to (the ones in nested attribute list),
are required because of cli.py parser issue, maybe Kuba knows a better way to
prevent the issue?
Basically, without those values, cli.py brakes on parsing responses, after
every "jump" to nested attribute list it is assigning first attribute there
with value=0, thus there is a need to assign a proper value, same as it is on
'main' attribute list.

>
>>+      -
>>+        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: source-pin-idx
>>+        type: u32
>>+      -
>>+        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
>>+        type: nest
>>+        value: 12
>>+        multi-attr: true
>>+        nested-attributes: pin
>
>This does not belong here.
>

What do you mean?
With device-get 'do' request the list of pins connected to the dpll is
returned, each pin is nested in this attribute.
This is required by parser to work.

>
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+        value: 21
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-dpll-caps
>>+        type: u32
>>+        value: 26
>
>All these 3 do not belong here are well.
>

Same as above explanation.

>
>
>>+  -
>>+    name: pin
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: device
>>+        type: nest
>>+        value: 1
>>+        multi-attr: true
>>+        nested-attributes: device
>>+      -
>>+        name: pin-idx
>>+        type: u32
>>+        value: 13
>>+      -
>>+        name: pin-description
>>+        type: string
>>+      -
>>+        name: pin-type
>>+        type: u8
>>+        enum: pin-type
>>+      -
>>+        name: pin-direction
>>+        type: u8
>>+        enum: pin-direction
>>+      -
>>+        name: pin-frequency
>>+        type: u32
>>+      -
>>+        name: pin-frequency-supported
>>+        type: u32
>>+        multi-attr: true
>>+      -
>>+        name: pin-any-frequency-min
>>+        type: u32
>>+      -
>>+        name: pin-any-frequency-max
>>+        type: u32
>>+      -
>>+        name: pin-prio
>>+        type: u32
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent
>>+        type: nest
>>+        multi-attr: true
>
>Multiple parents? How is that supposed to work?
>

As we have agreed, MUXed pins can have multiple parents.
In our case:
/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-get --json '{"id": 0, "pin-idx":13}'
{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
                     {'bus-name': 'pci',
                      'dev-name': '0000:21:00.0_1',
                      'id': 1}],
          'pin-description': '0000:21:00.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'}}],
          'pin-rclk-device': '0000:21:00.0',
          'pin-type': {'doc': "ethernet port PHY's recovered clock",
                       'name': 'synce-eth-port'}}]}


>
>>+        nested-attributes: pin-parent
>>+        value: 23
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>>+        value: 25
>>+      -
>>+        name: pin-dpll-caps
>>+        type: u32
>
>Missing "enum: "
>

It is actually a bitmask, this is why didn't set as enum, with enum type
parser won't parse it.

>
>>+  -
>>+    name: pin-parent
>>+    subset-of: dpll
>>+    attributes:
>>+      -
>>+        name: pin-state
>>+        type: u8
>>+        value: 22
>>+        enum: pin-state
>>+      -
>>+        name: pin-parent-idx
>>+        type: u32
>>+        value: 24
>>+      -
>>+        name: pin-rclk-device
>>+        type: string
>
>Yeah, as I wrote in the other email, this really smells to
>have like a simple string like this. What is it supposed to be?
>

Yes, let's discuss there.

>
>>+
>>+
>>+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
>
>Shouldn't this be "device"?
>

It would brake the parser, again I hope Jakub Kicinski could take a look on
this.

>
>>+      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
>
>"device" here as well?
>

Same as above.

>
>>+      flags: [ admin-perm ]
>>+
>>+      do:
>>+        pre: dpll-pre-doit
>>+        post: dpll-post-doit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+            - mode
>
>Hmm, shouldn't source-pin-index be here as well?

No, there is no set for this.
For manual mode user selects the pin by setting enabled state on the one
he needs to recover signal from.

source-pin-index is read only, returns active source.

>
>>+
>>+    -
>>+      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
>
>"pin"?
>

Same case as with dpll/device above.

>
>>+      flags: [ admin-perm ]
>>+
>>+      do:
>>+        pre: dpll-pin-pre-doit
>>+        post: dpll-pin-post-doit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+            - pin-idx
>>+        reply:
>>+          attributes:
>>+            - pin
>>+
>>+      dump:
>>+        pre: dpll-pin-pre-dumpit
>>+        post: dpll-pin-post-dumpit
>>+        request:
>>+          attributes:
>>+            - id
>>+            - bus-name
>>+            - dev-name
>>+        reply:
>>+          attributes:
>>+            - pin
>>+
>>+    -
>>+      name: pin-set
>>+      doc: Set attributes of a target pin
>>+      attribute-set: dpll
>
>"pin"?
>

Same case as with dpll/device above.

>
>>+      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-parent-idx
>>+            - pin-state
>>+
>>+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..099d1e30ca7c
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.c
>>@@ -0,0 +1,126 @@
>>+// SPDX-License-Identifier: 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),
>
>Hmm, any idea why the generator does not put define name
>here instead of "5"?
>

Not really, it probably needs a fix for this.

>
>>+};
>>+
>>+/* 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_U32, },
>
>4.3GHz would be the limit, isn't it future proof to make this rather u64?
>

Sure, fixed.

>
>>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
>
>This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
>is last. I found that the order is not according to the enum in the yaml
>operation definition. Perhaps the generator can sort this?
>

Fixed. This related to the order on list of attributes expected on ops
definition.

>
>>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>>+};
>>+
>>+/* Ops table for dpll */
>>+static const struct genl_split_ops dpll_nl_ops[6] = {
>>+	{
>>+		.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..3a354dfb9a31
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_nl.h
>>@@ -0,0 +1,42 @@
>>+/* SPDX-License-Identifier: 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..ece6fe685d08
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,196 @@
>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>+/* 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
>>+
>>+#define DPLL_TEMP_DIVIDER	10
>
>Some comment to this define would be nice.
>

I added to the spec, but it is not generated by the regen script.

>
>>+#define DPLL_PIN_FREQ_1_HZ	1
>>+#define DPLL_PIN_FREQ_10_MHZ	10000000
>
>Have this as enum. Spell out "frequency" to be consistent with the
>related attribute name.
>

Sure, fixed.

>
>>+
>>+/**
>>+ * 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 (or
>is in
>
>"any valid source" perhaps?
>

Yes, fixed.

>
>>+ *   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)
>>+};
>>+
>>+/**
>>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
>
>For some of the enums, you say for what attribute it serves as a value
>set, for some you don't. Please unify the approach. I think it is
>valuable to say that for every enum.
>

Sure, fixed.

>
>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>>+ */
>>+enum dpll_pin_state {
>>+	DPLL_PIN_STATE_UNSPEC,
>>+	DPLL_PIN_STATE_CONNECTED,
>>+	DPLL_PIN_STATE_DISCONNECTED,
>>+
>>+	__DPLL_PIN_STATE_MAX,
>>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>>+};
>>+
>>+/**
>>+ * enum dpll_pin_direction - available pin direction
>>+ * @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)
>>+};
>>+
>>+/**
>>+ * enum dpll_mode - working-modes a dpll can support, differentiate if
>>and how
>>+ *   dpll selects one of its sources to syntonize with it
>>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>>+ */
>>+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 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_SOURCE_PIN_IDX,
>>+	DPLL_A_LOCK_STATUS,
>>+	DPLL_A_TEMP,
>>+	DPLL_A_CLOCK_ID,
>>+	DPLL_A_TYPE,
>>+	DPLL_A_PIN,
>>+	DPLL_A_PIN_IDX,
>>+	DPLL_A_PIN_DESCRIPTION,
>
>I commented this name in the other email. This does not describe
>anything. It is a label on a connector, isn't it? Why don't to call it
>"label"?
>

Sure, fixed.

>
>>+	DPLL_A_PIN_TYPE,
>>+	DPLL_A_PIN_DIRECTION,
>>+	DPLL_A_PIN_FREQUENCY,
>>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,
>
>Drop "any". Not good for anything.
>

Sure, fixed.

>
>>+	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,
>
>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>enum name.

Sure, fixed.

>
>>+
>>+	__DPLL_A_MAX,
>>+	DPLL_A_MAX = (__DPLL_A_MAX - 1)
>>+};
>>+
>>+enum {
>>+	DPLL_CMD_UNSPEC,
>>+	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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-16 13:15       ` Kubalewski, Arkadiusz
@ 2023-03-16 13:45         ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 13:45 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 3:44 PM
>>
>>Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
>>> drivers/dpll/dpll_nl.c                | 126 +++++++
>>> drivers/dpll/dpll_nl.h                |  42 +++
>>> include/uapi/linux/dpll.h             | 196 ++++++++++
>>> 4 files changed, 878 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..03e9f0e2d3d2
>>>--- /dev/null
>>>+++ b/Documentation/netlink/specs/dpll.yaml
>>>@@ -0,0 +1,514 @@
>>>+name: dpll
>>>+
>>>+doc: DPLL subsystem.
>>>+
>>>+definitions:
>>>+  -
>>>+    type: const
>>>+    name: temp-divider
>>>+    value: 10
>>>+  -
>>>+    type: const
>>>+    name: pin-freq-1-hz
>>>+    value: 1
>>>+  -
>>>+    type: const
>>>+    name: pin-freq-10-mhz
>>>+    value: 10000000
>>>+  -
>>>+    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 (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: enum
>>>+    name: pin-type
>>>+    doc: Enumerates 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-state
>>>+    doc: available pin modes
>>>+    entries:
>>>+      -
>>>+        name: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: connected
>>>+        doc: pin connected
>>>+      -
>>>+        name: disconnected
>>>+        doc: pin disconnected
>>>+    render-max: true
>>>+  -
>>>+    type: enum
>>>+    name: pin-direction
>>>+    doc: available pin direction
>>>+    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: enum
>>>+    name: mode
>>
>>Could you please sort the enums so they are in the same order as the
>>attribute which carries them? That would also put the device and pin
>>enums together.
>>
>
>Fixed.
>
>>
>>>+    doc: |
>>>+      working-modes a dpll can support, differentiate if and how dpll
>>>selects
>>>+      one of its sources to syntonize with it
>>>+    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: 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: 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
>>>+  -
>>>+    type: flags
>>>+    name: pin-caps
>>>+    doc: define capabilities of a pin
>>>+    entries:
>>>+      -
>>>+        name: direction-can-change
>>>+      -
>>>+        name: priority-can-change
>>>+      -
>>>+        name: state-can-change
>>>+
>>>+
>>>+attribute-sets:
>>>+  -
>>>+    name: dpll
>>>+    enum-name: dplla
>>>+    attributes:
>>>+      -
>>>+        name: device
>>>+        type: nest
>>>+        value: 1
>>>+        multi-attr: true
>>>+        nested-attributes: device
>>
>>What is this "device" and what is it good for? Smells like some leftover
>>and with the nested scheme looks quite odd.
>>
>
>No, it is nested attribute type, used when multiple devices are returned with
>netlink:
>
>- dump of device-get command where all devices are returned, each one nested
>inside it:
>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]

Okay, why is it nested here? The is one netlink msg per dpll device
instance. Is this the real output of you made that up?

Device nest should not be there for DEVICE_GET, does not make sense.


>
>- do/dump of pin-get, in case of shared pins, each pin contains number of dpll
>handles it connects with:
>[{'pin': [{'device': [{'bus-name': 'pci',
>                       'dev-name': '0000:21:00.0_0',
>                       'id': 0,
>                       'pin-prio': 6,
>                       'pin-state': {'doc': 'pin connected',
>                                     'name': 'connected'}},
>                      {'bus-name': 'pci',
>                       'dev-name': '0000:21:00.0_1',
>                       'id': 1,
>                       'pin-prio': 8,
>                       'pin-state': {'doc': 'pin connected',
>                                     'name': 'connected'}}],

Okay, here I understand it contains device specific pin items. Makes
sense!


>           'pin-direction': {'doc': 'pin used as a source of a signal',
>                             'name': 'source'},
>           'pin-frequency': 1,
>           'pin-frequency-supported': [1, 10000000],
>           'pin-idx': 0,
>	...
>
>>
>>>+      -
>>>+        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: source-pin-idx
>>>+        type: u32
>>>+      -
>>>+        name: lock-status
>>>+        type: u8
>>>+        enum: lock-status
>>>+      -
>>>+        name: temp
>>
>>Could you put some comment regarding the divider here?
>>
>
>Sure, fixed.
>
>>
>>>+        type: s32
>>>+      -
>>>+        name: clock-id
>>>+        type: u64
>>>+      -
>>>+        name: type
>>>+        type: u8
>>>+        enum: type
>>>+      -
>>>+        name: pin
>>>+        type: nest
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>>+        value: 12
>>>+      -
>>>+        name: pin-idx
>>>+        type: u32
>>>+      -
>>>+        name: pin-description
>>>+        type: string
>>>+      -
>>>+        name: pin-type
>>>+        type: u8
>>>+        enum: pin-type
>>>+      -
>>>+        name: pin-direction
>>>+        type: u8
>>>+        enum: pin-direction
>>>+      -
>>>+        name: pin-frequency
>>>+        type: u32
>>>+      -
>>>+        name: pin-frequency-supported
>>>+        type: u32
>>>+        multi-attr: true
>>>+      -
>>>+        name: pin-any-frequency-min
>>>+        type: u32
>>>+      -
>>>+        name: pin-any-frequency-max
>>>+        type: u32
>>
>>These 2 attrs somehow overlap with pin-frequency-supported,
>>which is a bit confusing, I think that pin-frequency-supported
>>should carry all supported frequencies. How about to have something
>>like:
>>        name: pin-frequency-supported
>>        type: nest
>>	multi-attr: true
>>        nested-attributes: pin-frequency-range
>>
>>and then:
>>      name: pin-frequency-range
>>      subset-of: dpll
>>      attributes:
>>        -
>>          name: pin-frequency-min
>>          type: u32
>>        -
>>          name: pin-frequency-max
>>          type: u32
>>          doc: should be put only to specify range when value differs
>>	       from pin-frequency-min
>>
>>That could carry list of frequencies and ranges, in this case multiple
>>ones if possibly needed.
>>
>
>Sure
>
>>
>>
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent
>>>+        type: nest
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>>+        value: 23
>>
>>Value 23? What's this?
>>You have it specified for some attrs all over the place.
>>What is the reason for it?
>>
>
>Actually this particular one is not needed (also value: 12 on pin above),
>I will remove those.
>But the others you are refering to (the ones in nested attribute list),
>are required because of cli.py parser issue, maybe Kuba knows a better way to
>prevent the issue?
>Basically, without those values, cli.py brakes on parsing responses, after
>every "jump" to nested attribute list it is assigning first attribute there
>with value=0, thus there is a need to assign a proper value, same as it is on
>'main' attribute list.

That's weird. Looks like a bug then?


>
>>
>>>+      -
>>>+        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: source-pin-idx
>>>+        type: u32
>>>+      -
>>>+        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
>>>+        type: nest
>>>+        value: 12
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>
>>This does not belong here.
>>
>
>What do you mean?
>With device-get 'do' request the list of pins connected to the dpll is
>returned, each pin is nested in this attribute.

No, wait a sec. You have 2 object types: device and pin. Each have
separate netlink CMDs to get and dump individual objects.
Don't mix those together like this. I thought it became clear in the
past. :/


>This is required by parser to work.
>
>>
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+        value: 21
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-dpll-caps
>>>+        type: u32
>>>+        value: 26
>>
>>All these 3 do not belong here are well.
>>
>
>Same as above explanation.

Same as above reply.


>
>>
>>
>>>+  -
>>>+    name: pin
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: device
>>>+        type: nest
>>>+        value: 1
>>>+        multi-attr: true
>>>+        nested-attributes: device
>>>+      -
>>>+        name: pin-idx
>>>+        type: u32
>>>+        value: 13
>>>+      -
>>>+        name: pin-description
>>>+        type: string
>>>+      -
>>>+        name: pin-type
>>>+        type: u8
>>>+        enum: pin-type
>>>+      -
>>>+        name: pin-direction
>>>+        type: u8
>>>+        enum: pin-direction
>>>+      -
>>>+        name: pin-frequency
>>>+        type: u32
>>>+      -
>>>+        name: pin-frequency-supported
>>>+        type: u32
>>>+        multi-attr: true
>>>+      -
>>>+        name: pin-any-frequency-min
>>>+        type: u32
>>>+      -
>>>+        name: pin-any-frequency-max
>>>+        type: u32
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent
>>>+        type: nest
>>>+        multi-attr: true
>>
>>Multiple parents? How is that supposed to work?
>>
>
>As we have agreed, MUXed pins can have multiple parents.
>In our case:
>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-get --json '{"id": 0, "pin-idx":13}'
>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>                     {'bus-name': 'pci',
>                      'dev-name': '0000:21:00.0_1',
>                      'id': 1}],
>          'pin-description': '0000:21:00.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'}}],
>          'pin-rclk-device': '0000:21:00.0',
>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>                       'name': 'synce-eth-port'}}]}

Got it, it is still a bit hard to me to follow this. Could you
perhaps extend the Documentation to describe in more details
with examples? Would help a lot for slower people like me to understand
what's what.


>
>
>>
>>>+        nested-attributes: pin-parent
>>>+        value: 23
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>>+        value: 25
>>>+      -
>>>+        name: pin-dpll-caps
>>>+        type: u32
>>
>>Missing "enum: "
>>
>
>It is actually a bitmask, this is why didn't set as enum, with enum type
>parser won't parse it.

Ah! Got it. Perhaps a docs note with the enum pointer then?


>
>>
>>>+  -
>>>+    name: pin-parent
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        value: 22
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent-idx
>>>+        type: u32
>>>+        value: 24
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>
>>Yeah, as I wrote in the other email, this really smells to
>>have like a simple string like this. What is it supposed to be?
>>
>
>Yes, let's discuss there.

Yep.

>
>>
>>>+
>>>+
>>>+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
>>
>>Shouldn't this be "device"?
>>
>
>It would brake the parser, again I hope Jakub Kicinski could take a look on
>this.

Odd.

>
>>
>>>+      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
>>
>>"device" here as well?
>>
>
>Same as above.
>
>>
>>>+      flags: [ admin-perm ]
>>>+
>>>+      do:
>>>+        pre: dpll-pre-doit
>>>+        post: dpll-post-doit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+            - mode
>>
>>Hmm, shouldn't source-pin-index be here as well?
>
>No, there is no set for this.
>For manual mode user selects the pin by setting enabled state on the one
>he needs to recover signal from.
>
>source-pin-index is read only, returns active source.

Okay, got it. Then why do we have this assymetric approach? Just have
the enabled state to serve the user to see which one is selected, no?
This would help to avoid confusion (like mine) and allow not to create
inconsistencies (like no pin enabled yet driver to return some source
pin index)


>
>>
>>>+
>>>+    -
>>>+      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
>>
>>"pin"?
>>
>
>Same case as with dpll/device above.
>
>>
>>>+      flags: [ admin-perm ]
>>>+
>>>+      do:
>>>+        pre: dpll-pin-pre-doit
>>>+        post: dpll-pin-post-doit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+            - pin-idx
>>>+        reply:
>>>+          attributes:
>>>+            - pin
>>>+
>>>+      dump:
>>>+        pre: dpll-pin-pre-dumpit
>>>+        post: dpll-pin-post-dumpit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+        reply:
>>>+          attributes:
>>>+            - pin
>>>+
>>>+    -
>>>+      name: pin-set
>>>+      doc: Set attributes of a target pin
>>>+      attribute-set: dpll
>>
>>"pin"?
>>
>
>Same case as with dpll/device above.
>
>>
>>>+      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-parent-idx
>>>+            - pin-state
>>>+
>>>+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..099d1e30ca7c
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.c
>>>@@ -0,0 +1,126 @@
>>>+// SPDX-License-Identifier: 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),
>>
>>Hmm, any idea why the generator does not put define name
>>here instead of "5"?
>>
>
>Not really, it probably needs a fix for this.

Yeah.


>
>>
>>>+};
>>>+
>>>+/* 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_U32, },
>>
>>4.3GHz would be the limit, isn't it future proof to make this rather u64?
>>
>
>Sure, fixed.
>
>>
>>>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>>>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>>>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
>>
>>This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
>>is last. I found that the order is not according to the enum in the yaml
>>operation definition. Perhaps the generator can sort this?
>>
>
>Fixed. This related to the order on list of attributes expected on ops
>definition.

Yep.


>
>>
>>>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>>>+};
>>>+
>>>+/* Ops table for dpll */
>>>+static const struct genl_split_ops dpll_nl_ops[6] = {
>>>+	{
>>>+		.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..3a354dfb9a31
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.h
>>>@@ -0,0 +1,42 @@
>>>+/* SPDX-License-Identifier: 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..ece6fe685d08
>>>--- /dev/null
>>>+++ b/include/uapi/linux/dpll.h
>>>@@ -0,0 +1,196 @@
>>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>+/* 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
>>>+
>>>+#define DPLL_TEMP_DIVIDER	10
>>
>>Some comment to this define would be nice.
>>
>
>I added to the spec, but it is not generated by the regen script.
>
>>
>>>+#define DPLL_PIN_FREQ_1_HZ	1
>>>+#define DPLL_PIN_FREQ_10_MHZ	10000000
>>
>>Have this as enum. Spell out "frequency" to be consistent with the
>>related attribute name.
>>
>
>Sure, fixed.
>
>>
>>>+
>>>+/**
>>>+ * 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 (or
>>is in
>>
>>"any valid source" perhaps?
>>
>
>Yes, fixed.
>
>>
>>>+ *   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)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
>>
>>For some of the enums, you say for what attribute it serves as a value
>>set, for some you don't. Please unify the approach. I think it is
>>valuable to say that for every enum.
>>
>
>Sure, fixed.
>
>>
>>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>>>+ */
>>>+enum dpll_pin_state {
>>>+	DPLL_PIN_STATE_UNSPEC,
>>>+	DPLL_PIN_STATE_CONNECTED,
>>>+	DPLL_PIN_STATE_DISCONNECTED,
>>>+
>>>+	__DPLL_PIN_STATE_MAX,
>>>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_pin_direction - available pin direction
>>>+ * @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)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_mode - working-modes a dpll can support, differentiate if
>>>and how
>>>+ *   dpll selects one of its sources to syntonize with it
>>>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>>>+ */
>>>+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 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_SOURCE_PIN_IDX,
>>>+	DPLL_A_LOCK_STATUS,
>>>+	DPLL_A_TEMP,
>>>+	DPLL_A_CLOCK_ID,
>>>+	DPLL_A_TYPE,
>>>+	DPLL_A_PIN,
>>>+	DPLL_A_PIN_IDX,
>>>+	DPLL_A_PIN_DESCRIPTION,
>>
>>I commented this name in the other email. This does not describe
>>anything. It is a label on a connector, isn't it? Why don't to call it
>>"label"?
>>
>
>Sure, fixed.
>
>>
>>>+	DPLL_A_PIN_TYPE,
>>>+	DPLL_A_PIN_DIRECTION,
>>>+	DPLL_A_PIN_FREQUENCY,
>>>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>
>>Drop "any". Not good for anything.
>>
>
>Sure, fixed.
>
>>
>>>+	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,
>>
>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>enum name.
>
>Sure, fixed.


Thanks for all your work on this!

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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-16 13:45         ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 13:45 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, March 14, 2023 3:44 PM
>>
>>Sun, Mar 12, 2023 at 03:28:02AM CET, 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 | 514 ++++++++++++++++++++++++++
>>> drivers/dpll/dpll_nl.c                | 126 +++++++
>>> drivers/dpll/dpll_nl.h                |  42 +++
>>> include/uapi/linux/dpll.h             | 196 ++++++++++
>>> 4 files changed, 878 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..03e9f0e2d3d2
>>>--- /dev/null
>>>+++ b/Documentation/netlink/specs/dpll.yaml
>>>@@ -0,0 +1,514 @@
>>>+name: dpll
>>>+
>>>+doc: DPLL subsystem.
>>>+
>>>+definitions:
>>>+  -
>>>+    type: const
>>>+    name: temp-divider
>>>+    value: 10
>>>+  -
>>>+    type: const
>>>+    name: pin-freq-1-hz
>>>+    value: 1
>>>+  -
>>>+    type: const
>>>+    name: pin-freq-10-mhz
>>>+    value: 10000000
>>>+  -
>>>+    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 (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: enum
>>>+    name: pin-type
>>>+    doc: Enumerates 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-state
>>>+    doc: available pin modes
>>>+    entries:
>>>+      -
>>>+        name: unspec
>>>+        doc: unspecified value
>>>+      -
>>>+        name: connected
>>>+        doc: pin connected
>>>+      -
>>>+        name: disconnected
>>>+        doc: pin disconnected
>>>+    render-max: true
>>>+  -
>>>+    type: enum
>>>+    name: pin-direction
>>>+    doc: available pin direction
>>>+    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: enum
>>>+    name: mode
>>
>>Could you please sort the enums so they are in the same order as the
>>attribute which carries them? That would also put the device and pin
>>enums together.
>>
>
>Fixed.
>
>>
>>>+    doc: |
>>>+      working-modes a dpll can support, differentiate if and how dpll
>>>selects
>>>+      one of its sources to syntonize with it
>>>+    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: 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: 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
>>>+  -
>>>+    type: flags
>>>+    name: pin-caps
>>>+    doc: define capabilities of a pin
>>>+    entries:
>>>+      -
>>>+        name: direction-can-change
>>>+      -
>>>+        name: priority-can-change
>>>+      -
>>>+        name: state-can-change
>>>+
>>>+
>>>+attribute-sets:
>>>+  -
>>>+    name: dpll
>>>+    enum-name: dplla
>>>+    attributes:
>>>+      -
>>>+        name: device
>>>+        type: nest
>>>+        value: 1
>>>+        multi-attr: true
>>>+        nested-attributes: device
>>
>>What is this "device" and what is it good for? Smells like some leftover
>>and with the nested scheme looks quite odd.
>>
>
>No, it is nested attribute type, used when multiple devices are returned with
>netlink:
>
>- dump of device-get command where all devices are returned, each one nested
>inside it:
>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]

Okay, why is it nested here? The is one netlink msg per dpll device
instance. Is this the real output of you made that up?

Device nest should not be there for DEVICE_GET, does not make sense.


>
>- do/dump of pin-get, in case of shared pins, each pin contains number of dpll
>handles it connects with:
>[{'pin': [{'device': [{'bus-name': 'pci',
>                       'dev-name': '0000:21:00.0_0',
>                       'id': 0,
>                       'pin-prio': 6,
>                       'pin-state': {'doc': 'pin connected',
>                                     'name': 'connected'}},
>                      {'bus-name': 'pci',
>                       'dev-name': '0000:21:00.0_1',
>                       'id': 1,
>                       'pin-prio': 8,
>                       'pin-state': {'doc': 'pin connected',
>                                     'name': 'connected'}}],

Okay, here I understand it contains device specific pin items. Makes
sense!


>           'pin-direction': {'doc': 'pin used as a source of a signal',
>                             'name': 'source'},
>           'pin-frequency': 1,
>           'pin-frequency-supported': [1, 10000000],
>           'pin-idx': 0,
>	...
>
>>
>>>+      -
>>>+        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: source-pin-idx
>>>+        type: u32
>>>+      -
>>>+        name: lock-status
>>>+        type: u8
>>>+        enum: lock-status
>>>+      -
>>>+        name: temp
>>
>>Could you put some comment regarding the divider here?
>>
>
>Sure, fixed.
>
>>
>>>+        type: s32
>>>+      -
>>>+        name: clock-id
>>>+        type: u64
>>>+      -
>>>+        name: type
>>>+        type: u8
>>>+        enum: type
>>>+      -
>>>+        name: pin
>>>+        type: nest
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>>+        value: 12
>>>+      -
>>>+        name: pin-idx
>>>+        type: u32
>>>+      -
>>>+        name: pin-description
>>>+        type: string
>>>+      -
>>>+        name: pin-type
>>>+        type: u8
>>>+        enum: pin-type
>>>+      -
>>>+        name: pin-direction
>>>+        type: u8
>>>+        enum: pin-direction
>>>+      -
>>>+        name: pin-frequency
>>>+        type: u32
>>>+      -
>>>+        name: pin-frequency-supported
>>>+        type: u32
>>>+        multi-attr: true
>>>+      -
>>>+        name: pin-any-frequency-min
>>>+        type: u32
>>>+      -
>>>+        name: pin-any-frequency-max
>>>+        type: u32
>>
>>These 2 attrs somehow overlap with pin-frequency-supported,
>>which is a bit confusing, I think that pin-frequency-supported
>>should carry all supported frequencies. How about to have something
>>like:
>>        name: pin-frequency-supported
>>        type: nest
>>	multi-attr: true
>>        nested-attributes: pin-frequency-range
>>
>>and then:
>>      name: pin-frequency-range
>>      subset-of: dpll
>>      attributes:
>>        -
>>          name: pin-frequency-min
>>          type: u32
>>        -
>>          name: pin-frequency-max
>>          type: u32
>>          doc: should be put only to specify range when value differs
>>	       from pin-frequency-min
>>
>>That could carry list of frequencies and ranges, in this case multiple
>>ones if possibly needed.
>>
>
>Sure
>
>>
>>
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent
>>>+        type: nest
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>>+        value: 23
>>
>>Value 23? What's this?
>>You have it specified for some attrs all over the place.
>>What is the reason for it?
>>
>
>Actually this particular one is not needed (also value: 12 on pin above),
>I will remove those.
>But the others you are refering to (the ones in nested attribute list),
>are required because of cli.py parser issue, maybe Kuba knows a better way to
>prevent the issue?
>Basically, without those values, cli.py brakes on parsing responses, after
>every "jump" to nested attribute list it is assigning first attribute there
>with value=0, thus there is a need to assign a proper value, same as it is on
>'main' attribute list.

That's weird. Looks like a bug then?


>
>>
>>>+      -
>>>+        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: source-pin-idx
>>>+        type: u32
>>>+      -
>>>+        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
>>>+        type: nest
>>>+        value: 12
>>>+        multi-attr: true
>>>+        nested-attributes: pin
>>
>>This does not belong here.
>>
>
>What do you mean?
>With device-get 'do' request the list of pins connected to the dpll is
>returned, each pin is nested in this attribute.

No, wait a sec. You have 2 object types: device and pin. Each have
separate netlink CMDs to get and dump individual objects.
Don't mix those together like this. I thought it became clear in the
past. :/


>This is required by parser to work.
>
>>
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+        value: 21
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-dpll-caps
>>>+        type: u32
>>>+        value: 26
>>
>>All these 3 do not belong here are well.
>>
>
>Same as above explanation.

Same as above reply.


>
>>
>>
>>>+  -
>>>+    name: pin
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: device
>>>+        type: nest
>>>+        value: 1
>>>+        multi-attr: true
>>>+        nested-attributes: device
>>>+      -
>>>+        name: pin-idx
>>>+        type: u32
>>>+        value: 13
>>>+      -
>>>+        name: pin-description
>>>+        type: string
>>>+      -
>>>+        name: pin-type
>>>+        type: u8
>>>+        enum: pin-type
>>>+      -
>>>+        name: pin-direction
>>>+        type: u8
>>>+        enum: pin-direction
>>>+      -
>>>+        name: pin-frequency
>>>+        type: u32
>>>+      -
>>>+        name: pin-frequency-supported
>>>+        type: u32
>>>+        multi-attr: true
>>>+      -
>>>+        name: pin-any-frequency-min
>>>+        type: u32
>>>+      -
>>>+        name: pin-any-frequency-max
>>>+        type: u32
>>>+      -
>>>+        name: pin-prio
>>>+        type: u32
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent
>>>+        type: nest
>>>+        multi-attr: true
>>
>>Multiple parents? How is that supposed to work?
>>
>
>As we have agreed, MUXed pins can have multiple parents.
>In our case:
>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-get --json '{"id": 0, "pin-idx":13}'
>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>                     {'bus-name': 'pci',
>                      'dev-name': '0000:21:00.0_1',
>                      'id': 1}],
>          'pin-description': '0000:21:00.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'}}],
>          'pin-rclk-device': '0000:21:00.0',
>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>                       'name': 'synce-eth-port'}}]}

Got it, it is still a bit hard to me to follow this. Could you
perhaps extend the Documentation to describe in more details
with examples? Would help a lot for slower people like me to understand
what's what.


>
>
>>
>>>+        nested-attributes: pin-parent
>>>+        value: 23
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>>+        value: 25
>>>+      -
>>>+        name: pin-dpll-caps
>>>+        type: u32
>>
>>Missing "enum: "
>>
>
>It is actually a bitmask, this is why didn't set as enum, with enum type
>parser won't parse it.

Ah! Got it. Perhaps a docs note with the enum pointer then?


>
>>
>>>+  -
>>>+    name: pin-parent
>>>+    subset-of: dpll
>>>+    attributes:
>>>+      -
>>>+        name: pin-state
>>>+        type: u8
>>>+        value: 22
>>>+        enum: pin-state
>>>+      -
>>>+        name: pin-parent-idx
>>>+        type: u32
>>>+        value: 24
>>>+      -
>>>+        name: pin-rclk-device
>>>+        type: string
>>
>>Yeah, as I wrote in the other email, this really smells to
>>have like a simple string like this. What is it supposed to be?
>>
>
>Yes, let's discuss there.

Yep.

>
>>
>>>+
>>>+
>>>+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
>>
>>Shouldn't this be "device"?
>>
>
>It would brake the parser, again I hope Jakub Kicinski could take a look on
>this.

Odd.

>
>>
>>>+      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
>>
>>"device" here as well?
>>
>
>Same as above.
>
>>
>>>+      flags: [ admin-perm ]
>>>+
>>>+      do:
>>>+        pre: dpll-pre-doit
>>>+        post: dpll-post-doit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+            - mode
>>
>>Hmm, shouldn't source-pin-index be here as well?
>
>No, there is no set for this.
>For manual mode user selects the pin by setting enabled state on the one
>he needs to recover signal from.
>
>source-pin-index is read only, returns active source.

Okay, got it. Then why do we have this assymetric approach? Just have
the enabled state to serve the user to see which one is selected, no?
This would help to avoid confusion (like mine) and allow not to create
inconsistencies (like no pin enabled yet driver to return some source
pin index)


>
>>
>>>+
>>>+    -
>>>+      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
>>
>>"pin"?
>>
>
>Same case as with dpll/device above.
>
>>
>>>+      flags: [ admin-perm ]
>>>+
>>>+      do:
>>>+        pre: dpll-pin-pre-doit
>>>+        post: dpll-pin-post-doit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+            - pin-idx
>>>+        reply:
>>>+          attributes:
>>>+            - pin
>>>+
>>>+      dump:
>>>+        pre: dpll-pin-pre-dumpit
>>>+        post: dpll-pin-post-dumpit
>>>+        request:
>>>+          attributes:
>>>+            - id
>>>+            - bus-name
>>>+            - dev-name
>>>+        reply:
>>>+          attributes:
>>>+            - pin
>>>+
>>>+    -
>>>+      name: pin-set
>>>+      doc: Set attributes of a target pin
>>>+      attribute-set: dpll
>>
>>"pin"?
>>
>
>Same case as with dpll/device above.
>
>>
>>>+      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-parent-idx
>>>+            - pin-state
>>>+
>>>+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..099d1e30ca7c
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.c
>>>@@ -0,0 +1,126 @@
>>>+// SPDX-License-Identifier: 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),
>>
>>Hmm, any idea why the generator does not put define name
>>here instead of "5"?
>>
>
>Not really, it probably needs a fix for this.

Yeah.


>
>>
>>>+};
>>>+
>>>+/* 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_U32, },
>>
>>4.3GHz would be the limit, isn't it future proof to make this rather u64?
>>
>
>Sure, fixed.
>
>>
>>>+	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
>>>+	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
>>>+	[DPLL_A_PIN_PARENT_IDX] = { .type = NLA_U32, },
>>
>>This is a bit odd. The size is DPLL_A_PIN_PARENT_IDX + 1, yet PIN_STATE
>>is last. I found that the order is not according to the enum in the yaml
>>operation definition. Perhaps the generator can sort this?
>>
>
>Fixed. This related to the order on list of attributes expected on ops
>definition.

Yep.


>
>>
>>>+	[DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 2),
>>>+};
>>>+
>>>+/* Ops table for dpll */
>>>+static const struct genl_split_ops dpll_nl_ops[6] = {
>>>+	{
>>>+		.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..3a354dfb9a31
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_nl.h
>>>@@ -0,0 +1,42 @@
>>>+/* SPDX-License-Identifier: 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..ece6fe685d08
>>>--- /dev/null
>>>+++ b/include/uapi/linux/dpll.h
>>>@@ -0,0 +1,196 @@
>>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>+/* 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
>>>+
>>>+#define DPLL_TEMP_DIVIDER	10
>>
>>Some comment to this define would be nice.
>>
>
>I added to the spec, but it is not generated by the regen script.
>
>>
>>>+#define DPLL_PIN_FREQ_1_HZ	1
>>>+#define DPLL_PIN_FREQ_10_MHZ	10000000
>>
>>Have this as enum. Spell out "frequency" to be consistent with the
>>related attribute name.
>>
>
>Sure, fixed.
>
>>
>>>+
>>>+/**
>>>+ * 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 (or
>>is in
>>
>>"any valid source" perhaps?
>>
>
>Yes, fixed.
>
>>
>>>+ *   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)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_pin_type - Enumerates 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_state - available pin modes
>>
>>For some of the enums, you say for what attribute it serves as a value
>>set, for some you don't. Please unify the approach. I think it is
>>valuable to say that for every enum.
>>
>
>Sure, fixed.
>
>>
>>>+ * @DPLL_PIN_STATE_UNSPEC: unspecified value
>>>+ * @DPLL_PIN_STATE_CONNECTED: pin connected
>>>+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected
>>>+ */
>>>+enum dpll_pin_state {
>>>+	DPLL_PIN_STATE_UNSPEC,
>>>+	DPLL_PIN_STATE_CONNECTED,
>>>+	DPLL_PIN_STATE_DISCONNECTED,
>>>+
>>>+	__DPLL_PIN_STATE_MAX,
>>>+	DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_pin_direction - available pin direction
>>>+ * @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)
>>>+};
>>>+
>>>+/**
>>>+ * enum dpll_mode - working-modes a dpll can support, differentiate if
>>>and how
>>>+ *   dpll selects one of its sources to syntonize with it
>>>+ * @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_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_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 dpll_pin_caps - define capabilities of a pin
>>>+ */
>>>+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 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_SOURCE_PIN_IDX,
>>>+	DPLL_A_LOCK_STATUS,
>>>+	DPLL_A_TEMP,
>>>+	DPLL_A_CLOCK_ID,
>>>+	DPLL_A_TYPE,
>>>+	DPLL_A_PIN,
>>>+	DPLL_A_PIN_IDX,
>>>+	DPLL_A_PIN_DESCRIPTION,
>>
>>I commented this name in the other email. This does not describe
>>anything. It is a label on a connector, isn't it? Why don't to call it
>>"label"?
>>
>
>Sure, fixed.
>
>>
>>>+	DPLL_A_PIN_TYPE,
>>>+	DPLL_A_PIN_DIRECTION,
>>>+	DPLL_A_PIN_FREQUENCY,
>>>+	DPLL_A_PIN_FREQUENCY_SUPPORTED,
>>>+	DPLL_A_PIN_ANY_FREQUENCY_MIN,
>>>+	DPLL_A_PIN_ANY_FREQUENCY_MAX,
>>
>>Drop "any". Not good for anything.
>>
>
>Sure, fixed.
>
>>
>>>+	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,
>>
>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>enum name.
>
>Sure, fixed.


Thanks for all your work on this!

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-16 13:46     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 13:46 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:

[...]


> Documentation/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++

Why this is under networking? It is not networking. Please move,
probably directly under "Documentation"

[...]

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

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

Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:

[...]


> Documentation/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++

Why this is under networking? It is not networking. Please move,
probably directly under "Documentation"

[...]

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+	int (*state_on_pin_get)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 enum dpll_pin_state *state,
>+				 struct netlink_ext_ack *extack);

Could this be rather called "state_on_device_get" or perhaps even better
just "state_get" (in sync with "prio_set/get") ?

This "od dpll" is a bit confusing, there is no such object.
We have "device" and "pin".


>+	int (*state_on_pin_set)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 const enum dpll_pin_state state,
>+				 struct netlink_ext_ack *extack);

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+	int (*state_on_pin_get)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 enum dpll_pin_state *state,
>+				 struct netlink_ext_ack *extack);

Could this be rather called "state_on_device_get" or perhaps even better
just "state_get" (in sync with "prio_set/get") ?

This "od dpll" is a bit confusing, there is no such object.
We have "device" and "pin".


>+	int (*state_on_pin_set)(const struct dpll_pin *pin,
>+				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,
>+				 const struct dpll_device *dpll,
>+				 const enum dpll_pin_state state,
>+				 struct netlink_ext_ack *extack);

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-16 13:45         ` Jiri Pirko
@ 2023-03-16 15:19           ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 15:19 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:

[...]


>>>>+      flags: [ admin-perm ]
>>>>+
>>>>+      do:
>>>>+        pre: dpll-pre-doit
>>>>+        post: dpll-post-doit
>>>>+        request:
>>>>+          attributes:
>>>>+            - id
>>>>+            - bus-name
>>>>+            - dev-name
>>>>+            - mode
>>>
>>>Hmm, shouldn't source-pin-index be here as well?
>>
>>No, there is no set for this.
>>For manual mode user selects the pin by setting enabled state on the one
>>he needs to recover signal from.
>>
>>source-pin-index is read only, returns active source.
>
>Okay, got it. Then why do we have this assymetric approach? Just have
>the enabled state to serve the user to see which one is selected, no?
>This would help to avoid confusion (like mine) and allow not to create
>inconsistencies (like no pin enabled yet driver to return some source
>pin index)

Actually, for mlx5 implementation, would be non-trivial to implement
this, as each of the pin/port is instantiated and controlled by separate
pci backend.

Could you please remove, it is not needed and has potential and real
issues.

[...]


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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-16 15:19           ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-16 15:19 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:

[...]


>>>>+      flags: [ admin-perm ]
>>>>+
>>>>+      do:
>>>>+        pre: dpll-pre-doit
>>>>+        post: dpll-post-doit
>>>>+        request:
>>>>+          attributes:
>>>>+            - id
>>>>+            - bus-name
>>>>+            - dev-name
>>>>+            - mode
>>>
>>>Hmm, shouldn't source-pin-index be here as well?
>>
>>No, there is no set for this.
>>For manual mode user selects the pin by setting enabled state on the one
>>he needs to recover signal from.
>>
>>source-pin-index is read only, returns active source.
>
>Okay, got it. Then why do we have this assymetric approach? Just have
>the enabled state to serve the user to see which one is selected, no?
>This would help to avoid confusion (like mine) and allow not to create
>inconsistencies (like no pin enabled yet driver to return some source
>pin index)

Actually, for mlx5 implementation, would be non-trivial to implement
this, as each of the pin/port is instantiated and controlled by separate
pci backend.

Could you please remove, it is not needed and has potential and real
issues.

[...]


_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 struct dpll_device_ops *ops, void *priv,

ops should be const


>+			 struct device *owner)

[...]


>+
>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,

ops should be const


>+		    const char *rclk_device_name)
>+{

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 struct dpll_device_ops *ops, void *priv,

ops should be const


>+			 struct device *owner)

[...]


>+
>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,

ops should be const


>+		    const char *rclk_device_name)
>+{

[...]

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-16 13:45         ` Jiri Pirko
@ 2023-03-17  0:52           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17  0:52 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 2:45 PM
>

[...]

>>>>+attribute-sets:
>>>>+  -
>>>>+    name: dpll
>>>>+    enum-name: dplla
>>>>+    attributes:
>>>>+      -
>>>>+        name: device
>>>>+        type: nest
>>>>+        value: 1
>>>>+        multi-attr: true
>>>>+        nested-attributes: device
>>>
>>>What is this "device" and what is it good for? Smells like some leftover
>>>and with the nested scheme looks quite odd.
>>>
>>
>>No, it is nested attribute type, used when multiple devices are returned
>>with netlink:
>>
>>- dump of device-get command where all devices are returned, each one nested
>>inside it:
>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>
>Okay, why is it nested here? The is one netlink msg per dpll device
>instance. Is this the real output of you made that up?
>
>Device nest should not be there for DEVICE_GET, does not make sense.
>

This was returned by CLI parser on ice with cmd:
$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
--dump device-get

Please note this relates to 'dump' request , it is rather expected that there
are multiple dplls returned, thus we need a nest attribute for each one.

>
>>
>>- do/dump of pin-get, in case of shared pins, each pin contains number of
>dpll
>>handles it connects with:
>>[{'pin': [{'device': [{'bus-name': 'pci',
>>                       'dev-name': '0000:21:00.0_0',
>>                       'id': 0,
>>                       'pin-prio': 6,
>>                       'pin-state': {'doc': 'pin connected',
>>                                     'name': 'connected'}},
>>                      {'bus-name': 'pci',
>>                       'dev-name': '0000:21:00.0_1',
>>                       'id': 1,
>>                       'pin-prio': 8,
>>                       'pin-state': {'doc': 'pin connected',
>>                                     'name': 'connected'}}],
>
>Okay, here I understand it contains device specific pin items. Makes
>sense!
>

Good.

[...]

>>
>>>
>>>
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent
>>>>+        type: nest
>>>>+        multi-attr: true
>>>>+        nested-attributes: pin
>>>>+        value: 23
>>>
>>>Value 23? What's this?
>>>You have it specified for some attrs all over the place.
>>>What is the reason for it?
>>>
>>
>>Actually this particular one is not needed (also value: 12 on pin above),
>>I will remove those.
>>But the others you are refering to (the ones in nested attribute list),
>>are required because of cli.py parser issue, maybe Kuba knows a better way
>to
>>prevent the issue?
>>Basically, without those values, cli.py brakes on parsing responses, after
>>every "jump" to nested attribute list it is assigning first attribute
>there
>>with value=0, thus there is a need to assign a proper value, same as it is
>on
>>'main' attribute list.
>
>That's weird. Looks like a bug then?
>

Guess we could call it a bug, I haven't investigated the parser that much,
AFAIR, other specs are doing the same way.

>
>>
>>>
>>>>+      -
>>>>+        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: source-pin-idx
>>>>+        type: u32
>>>>+      -
>>>>+        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
>>>>+        type: nest
>>>>+        value: 12
>>>>+        multi-attr: true
>>>>+        nested-attributes: pin
>>>
>>>This does not belong here.
>>>
>>
>>What do you mean?
>>With device-get 'do' request the list of pins connected to the dpll is
>>returned, each pin is nested in this attribute.
>
>No, wait a sec. You have 2 object types: device and pin. Each have
>separate netlink CMDs to get and dump individual objects.
>Don't mix those together like this. I thought it became clear in the
>past. :/
>

For pins we must, as pins without a handle to a dpll are pointless.
Same as a dpll without pins, right?

'do' of DEVICE_GET could just dump it's own status, without the list of pins,
but it feels easier for handling it's state on userspace counterpart if that
command also returns currently registered pins. Don't you think so?

>
>>This is required by parser to work.
>>
>>>
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+        value: 21
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-dpll-caps
>>>>+        type: u32
>>>>+        value: 26
>>>
>>>All these 3 do not belong here are well.
>>>
>>
>>Same as above explanation.
>
>Same as above reply.
>
>
>>
>>>
>>>
>>>>+  -
>>>>+    name: pin
>>>>+    subset-of: dpll
>>>>+    attributes:
>>>>+      -
>>>>+        name: device
>>>>+        type: nest
>>>>+        value: 1
>>>>+        multi-attr: true
>>>>+        nested-attributes: device
>>>>+      -
>>>>+        name: pin-idx
>>>>+        type: u32
>>>>+        value: 13
>>>>+      -
>>>>+        name: pin-description
>>>>+        type: string
>>>>+      -
>>>>+        name: pin-type
>>>>+        type: u8
>>>>+        enum: pin-type
>>>>+      -
>>>>+        name: pin-direction
>>>>+        type: u8
>>>>+        enum: pin-direction
>>>>+      -
>>>>+        name: pin-frequency
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-frequency-supported
>>>>+        type: u32
>>>>+        multi-attr: true
>>>>+      -
>>>>+        name: pin-any-frequency-min
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-any-frequency-max
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent
>>>>+        type: nest
>>>>+        multi-attr: true
>>>
>>>Multiple parents? How is that supposed to work?
>>>
>>
>>As we have agreed, MUXed pins can have multiple parents.
>>In our case:
>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>pin-get --json '{"id": 0, "pin-idx":13}'
>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>'id': 0},
>>                     {'bus-name': 'pci',
>>                      'dev-name': '0000:21:00.0_1',
>>                      'id': 1}],
>>          'pin-description': '0000:21:00.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'}}],
>>          'pin-rclk-device': '0000:21:00.0',
>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>                       'name': 'synce-eth-port'}}]}
>
>Got it, it is still a bit hard to me to follow this. Could you
>perhaps extend the Documentation to describe in more details
>with examples? Would help a lot for slower people like me to understand
>what's what.
>

Actually this is already explained in "MUX-type pins" paragraph of
Documentation/networking/dpll.rst.
Do we want to duplicate this explanation here?


>
>>
>>
>>>
>>>>+        nested-attributes: pin-parent
>>>>+        value: 23
>>>>+      -
>>>>+        name: pin-rclk-device
>>>>+        type: string
>>>>+        value: 25
>>>>+      -
>>>>+        name: pin-dpll-caps
>>>>+        type: u32
>>>
>>>Missing "enum: "
>>>
>>
>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>parser won't parse it.
>
>Ah! Got it. Perhaps a docs note with the enum pointer then?
>

Same as above, explained in Documentation/networking/dpll.rst, do wan't to
duplicate?

>
>>
>>>
>>>>+  -
>>>>+    name: pin-parent
>>>>+    subset-of: dpll
>>>>+    attributes:
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        value: 22
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent-idx
>>>>+        type: u32
>>>>+        value: 24
>>>>+      -
>>>>+        name: pin-rclk-device
>>>>+        type: string
>>>
>>>Yeah, as I wrote in the other email, this really smells to
>>>have like a simple string like this. What is it supposed to be?
>>>
>>
>>Yes, let's discuss there.
>
>Yep.
>
>>
>>>
>>>>+
>>>>+
>>>>+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
>>>
>>>Shouldn't this be "device"?
>>>
>>
>>It would brake the parser, again I hope Jakub Kicinski could take a look
>>on this.
>
>Odd.
>

Yes, seems a bit odd.

>>
>>>
>>>>+      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
>>>
>>>"device" here as well?
>>>
>>
>>Same as above.
>>
>>>
>>>>+      flags: [ admin-perm ]
>>>>+
>>>>+      do:
>>>>+        pre: dpll-pre-doit
>>>>+        post: dpll-post-doit
>>>>+        request:
>>>>+          attributes:
>>>>+            - id
>>>>+            - bus-name
>>>>+            - dev-name
>>>>+            - mode
>>>
>>>Hmm, shouldn't source-pin-index be here as well?
>>
>>No, there is no set for this.
>>For manual mode user selects the pin by setting enabled state on the one
>>he needs to recover signal from.
>>
>>source-pin-index is read only, returns active source.
>
>Okay, got it. Then why do we have this assymetric approach? Just have
>the enabled state to serve the user to see which one is selected, no?
>This would help to avoid confusion (like mine) and allow not to create
>inconsistencies (like no pin enabled yet driver to return some source
>pin index)
>

This is due to automatic mode were multiple pins are enabled, but actual
selection is done on hardware level with priorities.

[...]

>>>>+
>>>>+/* 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),
>>>
>>>Hmm, any idea why the generator does not put define name
>>>here instead of "5"?
>>>
>>
>>Not really, it probably needs a fix for this.
>
>Yeah.
>

Well, once we done with review maybe we could also fix those, or ask
Jakub if he could help :)


[...]

>>>
>>>>+	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,
>>>
>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>enum name.
>>
>>Sure, fixed.
>
>
>Thanks for all your work on this!

Thanks for a great review! :)

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

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17  0:52           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17  0:52 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 2:45 PM
>

[...]

>>>>+attribute-sets:
>>>>+  -
>>>>+    name: dpll
>>>>+    enum-name: dplla
>>>>+    attributes:
>>>>+      -
>>>>+        name: device
>>>>+        type: nest
>>>>+        value: 1
>>>>+        multi-attr: true
>>>>+        nested-attributes: device
>>>
>>>What is this "device" and what is it good for? Smells like some leftover
>>>and with the nested scheme looks quite odd.
>>>
>>
>>No, it is nested attribute type, used when multiple devices are returned
>>with netlink:
>>
>>- dump of device-get command where all devices are returned, each one nested
>>inside it:
>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>
>Okay, why is it nested here? The is one netlink msg per dpll device
>instance. Is this the real output of you made that up?
>
>Device nest should not be there for DEVICE_GET, does not make sense.
>

This was returned by CLI parser on ice with cmd:
$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
--dump device-get

Please note this relates to 'dump' request , it is rather expected that there
are multiple dplls returned, thus we need a nest attribute for each one.

>
>>
>>- do/dump of pin-get, in case of shared pins, each pin contains number of
>dpll
>>handles it connects with:
>>[{'pin': [{'device': [{'bus-name': 'pci',
>>                       'dev-name': '0000:21:00.0_0',
>>                       'id': 0,
>>                       'pin-prio': 6,
>>                       'pin-state': {'doc': 'pin connected',
>>                                     'name': 'connected'}},
>>                      {'bus-name': 'pci',
>>                       'dev-name': '0000:21:00.0_1',
>>                       'id': 1,
>>                       'pin-prio': 8,
>>                       'pin-state': {'doc': 'pin connected',
>>                                     'name': 'connected'}}],
>
>Okay, here I understand it contains device specific pin items. Makes
>sense!
>

Good.

[...]

>>
>>>
>>>
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent
>>>>+        type: nest
>>>>+        multi-attr: true
>>>>+        nested-attributes: pin
>>>>+        value: 23
>>>
>>>Value 23? What's this?
>>>You have it specified for some attrs all over the place.
>>>What is the reason for it?
>>>
>>
>>Actually this particular one is not needed (also value: 12 on pin above),
>>I will remove those.
>>But the others you are refering to (the ones in nested attribute list),
>>are required because of cli.py parser issue, maybe Kuba knows a better way
>to
>>prevent the issue?
>>Basically, without those values, cli.py brakes on parsing responses, after
>>every "jump" to nested attribute list it is assigning first attribute
>there
>>with value=0, thus there is a need to assign a proper value, same as it is
>on
>>'main' attribute list.
>
>That's weird. Looks like a bug then?
>

Guess we could call it a bug, I haven't investigated the parser that much,
AFAIR, other specs are doing the same way.

>
>>
>>>
>>>>+      -
>>>>+        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: source-pin-idx
>>>>+        type: u32
>>>>+      -
>>>>+        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
>>>>+        type: nest
>>>>+        value: 12
>>>>+        multi-attr: true
>>>>+        nested-attributes: pin
>>>
>>>This does not belong here.
>>>
>>
>>What do you mean?
>>With device-get 'do' request the list of pins connected to the dpll is
>>returned, each pin is nested in this attribute.
>
>No, wait a sec. You have 2 object types: device and pin. Each have
>separate netlink CMDs to get and dump individual objects.
>Don't mix those together like this. I thought it became clear in the
>past. :/
>

For pins we must, as pins without a handle to a dpll are pointless.
Same as a dpll without pins, right?

'do' of DEVICE_GET could just dump it's own status, without the list of pins,
but it feels easier for handling it's state on userspace counterpart if that
command also returns currently registered pins. Don't you think so?

>
>>This is required by parser to work.
>>
>>>
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+        value: 21
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-dpll-caps
>>>>+        type: u32
>>>>+        value: 26
>>>
>>>All these 3 do not belong here are well.
>>>
>>
>>Same as above explanation.
>
>Same as above reply.
>
>
>>
>>>
>>>
>>>>+  -
>>>>+    name: pin
>>>>+    subset-of: dpll
>>>>+    attributes:
>>>>+      -
>>>>+        name: device
>>>>+        type: nest
>>>>+        value: 1
>>>>+        multi-attr: true
>>>>+        nested-attributes: device
>>>>+      -
>>>>+        name: pin-idx
>>>>+        type: u32
>>>>+        value: 13
>>>>+      -
>>>>+        name: pin-description
>>>>+        type: string
>>>>+      -
>>>>+        name: pin-type
>>>>+        type: u8
>>>>+        enum: pin-type
>>>>+      -
>>>>+        name: pin-direction
>>>>+        type: u8
>>>>+        enum: pin-direction
>>>>+      -
>>>>+        name: pin-frequency
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-frequency-supported
>>>>+        type: u32
>>>>+        multi-attr: true
>>>>+      -
>>>>+        name: pin-any-frequency-min
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-any-frequency-max
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-prio
>>>>+        type: u32
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent
>>>>+        type: nest
>>>>+        multi-attr: true
>>>
>>>Multiple parents? How is that supposed to work?
>>>
>>
>>As we have agreed, MUXed pins can have multiple parents.
>>In our case:
>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>pin-get --json '{"id": 0, "pin-idx":13}'
>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>'id': 0},
>>                     {'bus-name': 'pci',
>>                      'dev-name': '0000:21:00.0_1',
>>                      'id': 1}],
>>          'pin-description': '0000:21:00.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'}}],
>>          'pin-rclk-device': '0000:21:00.0',
>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>                       'name': 'synce-eth-port'}}]}
>
>Got it, it is still a bit hard to me to follow this. Could you
>perhaps extend the Documentation to describe in more details
>with examples? Would help a lot for slower people like me to understand
>what's what.
>

Actually this is already explained in "MUX-type pins" paragraph of
Documentation/networking/dpll.rst.
Do we want to duplicate this explanation here?


>
>>
>>
>>>
>>>>+        nested-attributes: pin-parent
>>>>+        value: 23
>>>>+      -
>>>>+        name: pin-rclk-device
>>>>+        type: string
>>>>+        value: 25
>>>>+      -
>>>>+        name: pin-dpll-caps
>>>>+        type: u32
>>>
>>>Missing "enum: "
>>>
>>
>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>parser won't parse it.
>
>Ah! Got it. Perhaps a docs note with the enum pointer then?
>

Same as above, explained in Documentation/networking/dpll.rst, do wan't to
duplicate?

>
>>
>>>
>>>>+  -
>>>>+    name: pin-parent
>>>>+    subset-of: dpll
>>>>+    attributes:
>>>>+      -
>>>>+        name: pin-state
>>>>+        type: u8
>>>>+        value: 22
>>>>+        enum: pin-state
>>>>+      -
>>>>+        name: pin-parent-idx
>>>>+        type: u32
>>>>+        value: 24
>>>>+      -
>>>>+        name: pin-rclk-device
>>>>+        type: string
>>>
>>>Yeah, as I wrote in the other email, this really smells to
>>>have like a simple string like this. What is it supposed to be?
>>>
>>
>>Yes, let's discuss there.
>
>Yep.
>
>>
>>>
>>>>+
>>>>+
>>>>+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
>>>
>>>Shouldn't this be "device"?
>>>
>>
>>It would brake the parser, again I hope Jakub Kicinski could take a look
>>on this.
>
>Odd.
>

Yes, seems a bit odd.

>>
>>>
>>>>+      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
>>>
>>>"device" here as well?
>>>
>>
>>Same as above.
>>
>>>
>>>>+      flags: [ admin-perm ]
>>>>+
>>>>+      do:
>>>>+        pre: dpll-pre-doit
>>>>+        post: dpll-post-doit
>>>>+        request:
>>>>+          attributes:
>>>>+            - id
>>>>+            - bus-name
>>>>+            - dev-name
>>>>+            - mode
>>>
>>>Hmm, shouldn't source-pin-index be here as well?
>>
>>No, there is no set for this.
>>For manual mode user selects the pin by setting enabled state on the one
>>he needs to recover signal from.
>>
>>source-pin-index is read only, returns active source.
>
>Okay, got it. Then why do we have this assymetric approach? Just have
>the enabled state to serve the user to see which one is selected, no?
>This would help to avoid confusion (like mine) and allow not to create
>inconsistencies (like no pin enabled yet driver to return some source
>pin index)
>

This is due to automatic mode were multiple pins are enabled, but actual
selection is done on hardware level with priorities.

[...]

>>>>+
>>>>+/* 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),
>>>
>>>Hmm, any idea why the generator does not put define name
>>>here instead of "5"?
>>>
>>
>>Not really, it probably needs a fix for this.
>
>Yeah.
>

Well, once we done with review maybe we could also fix those, or ask
Jakub if he could help :)


[...]

>>>
>>>>+	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,
>>>
>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>enum name.
>>
>>Sure, fixed.
>
>
>Thanks for all your work on this!

Thanks for a great review! :)

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-16 15:19           ` Jiri Pirko
@ 2023-03-17  0:53             ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17  0:53 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 4:20 PM
>
>Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>[...]
>
>
>>>>>+      flags: [ admin-perm ]
>>>>>+
>>>>>+      do:
>>>>>+        pre: dpll-pre-doit
>>>>>+        post: dpll-post-doit
>>>>>+        request:
>>>>>+          attributes:
>>>>>+            - id
>>>>>+            - bus-name
>>>>>+            - dev-name
>>>>>+            - mode
>>>>
>>>>Hmm, shouldn't source-pin-index be here as well?
>>>
>>>No, there is no set for this.
>>>For manual mode user selects the pin by setting enabled state on the one
>>>he needs to recover signal from.
>>>
>>>source-pin-index is read only, returns active source.
>>
>>Okay, got it. Then why do we have this assymetric approach? Just have
>>the enabled state to serve the user to see which one is selected, no?
>>This would help to avoid confusion (like mine) and allow not to create
>>inconsistencies (like no pin enabled yet driver to return some source
>>pin index)
>
>Actually, for mlx5 implementation, would be non-trivial to implement
>this, as each of the pin/port is instantiated and controlled by separate
>pci backend.
>
>Could you please remove, it is not needed and has potential and real
>issues.
>
>[...]

Sorry I cannot, for priority based automatic selection mode multiple sources
are enabled at any time - selection is done automatically by the chip.
Thus for that case, this attribute is only way of getting an active source.
Although, maybe we could allow driver to not implement it, would this help
for your case? As it seems only required for automatic mode selection.

Thank you,
Arkadiusz

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

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17  0:53             ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17  0:53 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 4:20 PM
>
>Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>[...]
>
>
>>>>>+      flags: [ admin-perm ]
>>>>>+
>>>>>+      do:
>>>>>+        pre: dpll-pre-doit
>>>>>+        post: dpll-post-doit
>>>>>+        request:
>>>>>+          attributes:
>>>>>+            - id
>>>>>+            - bus-name
>>>>>+            - dev-name
>>>>>+            - mode
>>>>
>>>>Hmm, shouldn't source-pin-index be here as well?
>>>
>>>No, there is no set for this.
>>>For manual mode user selects the pin by setting enabled state on the one
>>>he needs to recover signal from.
>>>
>>>source-pin-index is read only, returns active source.
>>
>>Okay, got it. Then why do we have this assymetric approach? Just have
>>the enabled state to serve the user to see which one is selected, no?
>>This would help to avoid confusion (like mine) and allow not to create
>>inconsistencies (like no pin enabled yet driver to return some source
>>pin index)
>
>Actually, for mlx5 implementation, would be non-trivial to implement
>this, as each of the pin/port is instantiated and controlled by separate
>pci backend.
>
>Could you please remove, it is not needed and has potential and real
>issues.
>
>[...]

Sorry I cannot, for priority based automatic selection mode multiple sources
are enabled at any time - selection is done automatically by the chip.
Thus for that case, this attribute is only way of getting an active source.
Although, maybe we could allow driver to not implement it, would this help
for your case? As it seems only required for automatic mode selection.

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] 202+ messages in thread

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

Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, March 16, 2023 2:45 PM
>>
>
>[...]
>
>>>>>+attribute-sets:
>>>>>+  -
>>>>>+    name: dpll
>>>>>+    enum-name: dplla
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: device
>>>>>+        type: nest
>>>>>+        value: 1
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: device
>>>>
>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>and with the nested scheme looks quite odd.
>>>>
>>>
>>>No, it is nested attribute type, used when multiple devices are returned
>>>with netlink:
>>>
>>>- dump of device-get command where all devices are returned, each one nested
>>>inside it:
>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>>
>>Okay, why is it nested here? The is one netlink msg per dpll device
>>instance. Is this the real output of you made that up?
>>
>>Device nest should not be there for DEVICE_GET, does not make sense.
>>
>
>This was returned by CLI parser on ice with cmd:
>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>--dump device-get
>
>Please note this relates to 'dump' request , it is rather expected that there
>are multiple dplls returned, thus we need a nest attribute for each one.

No, you definitelly don't need to nest them. Dump format and get format
should be exactly the same. Please remove the nest.

See how that is done in devlink for example: devlink_nl_fill()
This functions fills up one object in the dump. No nesting.
I'm not aware of such nesting approach anywhere in kernel dumps, does
not make sense at all.


>
>>
>>>
>>>- do/dump of pin-get, in case of shared pins, each pin contains number of
>>dpll
>>>handles it connects with:
>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>                       'dev-name': '0000:21:00.0_0',
>>>                       'id': 0,
>>>                       'pin-prio': 6,
>>>                       'pin-state': {'doc': 'pin connected',
>>>                                     'name': 'connected'}},
>>>                      {'bus-name': 'pci',
>>>                       'dev-name': '0000:21:00.0_1',
>>>                       'id': 1,
>>>                       'pin-prio': 8,
>>>                       'pin-state': {'doc': 'pin connected',
>>>                                     'name': 'connected'}}],
>>
>>Okay, here I understand it contains device specific pin items. Makes
>>sense!
>>
>
>Good.

Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
reason as above.
I don't see a need for DPLL_A_PIN attr existence, please remove it.




>
>[...]
>
>>>
>>>>
>>>>
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent
>>>>>+        type: nest
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: pin
>>>>>+        value: 23
>>>>
>>>>Value 23? What's this?
>>>>You have it specified for some attrs all over the place.
>>>>What is the reason for it?
>>>>
>>>
>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>I will remove those.
>>>But the others you are refering to (the ones in nested attribute list),
>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>to
>>>prevent the issue?
>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>every "jump" to nested attribute list it is assigning first attribute
>>there
>>>with value=0, thus there is a need to assign a proper value, same as it is
>>on
>>>'main' attribute list.
>>
>>That's weird. Looks like a bug then?
>>
>
>Guess we could call it a bug, I haven't investigated the parser that much,
>AFAIR, other specs are doing the same way.
>
>>
>>>
>>>>
>>>>>+      -
>>>>>+        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: source-pin-idx
>>>>>+        type: u32
>>>>>+      -
>>>>>+        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
>>>>>+        type: nest
>>>>>+        value: 12
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: pin
>>>>
>>>>This does not belong here.
>>>>
>>>
>>>What do you mean?
>>>With device-get 'do' request the list of pins connected to the dpll is
>>>returned, each pin is nested in this attribute.
>>
>>No, wait a sec. You have 2 object types: device and pin. Each have
>>separate netlink CMDs to get and dump individual objects.
>>Don't mix those together like this. I thought it became clear in the
>>past. :/
>>
>
>For pins we must, as pins without a handle to a dpll are pointless.

I'm not talking about per device specific items for pins (state and
prio). That is something else, it's a pin-device tuple. Completely fine.



>Same as a dpll without pins, right?
>
>'do' of DEVICE_GET could just dump it's own status, without the list of pins,

Yes please.


>but it feels easier for handling it's state on userspace counterpart if that
>command also returns currently registered pins. Don't you think so?

No, definitelly not. Please make the object separation clear. Device and
pins are different objects, they have different commands to work with.
Don't mix them together.


>
>>
>>>This is required by parser to work.
>>>
>>>>
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+        value: 21
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-dpll-caps
>>>>>+        type: u32
>>>>>+        value: 26
>>>>
>>>>All these 3 do not belong here are well.
>>>>
>>>
>>>Same as above explanation.
>>
>>Same as above reply.
>>
>>
>>>
>>>>
>>>>
>>>>>+  -
>>>>>+    name: pin
>>>>>+    subset-of: dpll
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: device
>>>>>+        type: nest
>>>>>+        value: 1
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: device
>>>>>+      -
>>>>>+        name: pin-idx
>>>>>+        type: u32
>>>>>+        value: 13
>>>>>+      -
>>>>>+        name: pin-description
>>>>>+        type: string
>>>>>+      -
>>>>>+        name: pin-type
>>>>>+        type: u8
>>>>>+        enum: pin-type
>>>>>+      -
>>>>>+        name: pin-direction
>>>>>+        type: u8
>>>>>+        enum: pin-direction
>>>>>+      -
>>>>>+        name: pin-frequency
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-frequency-supported
>>>>>+        type: u32
>>>>>+        multi-attr: true
>>>>>+      -
>>>>>+        name: pin-any-frequency-min
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-any-frequency-max
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent
>>>>>+        type: nest
>>>>>+        multi-attr: true
>>>>
>>>>Multiple parents? How is that supposed to work?
>>>>
>>>
>>>As we have agreed, MUXed pins can have multiple parents.
>>>In our case:
>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>'id': 0},
>>>                     {'bus-name': 'pci',
>>>                      'dev-name': '0000:21:00.0_1',
>>>                      'id': 1}],
>>>          'pin-description': '0000:21:00.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'}}],
>>>          'pin-rclk-device': '0000:21:00.0',
>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>                       'name': 'synce-eth-port'}}]}
>>
>>Got it, it is still a bit hard to me to follow this. Could you
>>perhaps extend the Documentation to describe in more details
>>with examples? Would help a lot for slower people like me to understand
>>what's what.
>>
>
>Actually this is already explained in "MUX-type pins" paragraph of
>Documentation/networking/dpll.rst.
>Do we want to duplicate this explanation here?

No, please extend the docs. As I wrote above, could you add some
examples, like the one you pasted above. Examples always help to
undestand things much better.


>
>
>>
>>>
>>>
>>>>
>>>>>+        nested-attributes: pin-parent
>>>>>+        value: 23
>>>>>+      -
>>>>>+        name: pin-rclk-device
>>>>>+        type: string
>>>>>+        value: 25
>>>>>+      -
>>>>>+        name: pin-dpll-caps
>>>>>+        type: u32
>>>>
>>>>Missing "enum: "
>>>>
>>>
>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>parser won't parse it.
>>
>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>
>
>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>duplicate?

For this, yes. Some small doc note here would be quite convenient.

Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
to do nicer validation as well.


>
>>
>>>
>>>>
>>>>>+  -
>>>>>+    name: pin-parent
>>>>>+    subset-of: dpll
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        value: 22
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent-idx
>>>>>+        type: u32
>>>>>+        value: 24
>>>>>+      -
>>>>>+        name: pin-rclk-device
>>>>>+        type: string
>>>>
>>>>Yeah, as I wrote in the other email, this really smells to
>>>>have like a simple string like this. What is it supposed to be?
>>>>
>>>
>>>Yes, let's discuss there.
>>
>>Yep.
>>
>>>
>>>>
>>>>>+
>>>>>+
>>>>>+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
>>>>
>>>>Shouldn't this be "device"?
>>>>
>>>
>>>It would brake the parser, again I hope Jakub Kicinski could take a look
>>>on this.
>>
>>Odd.
>>
>
>Yes, seems a bit odd.
>
>>>
>>>>
>>>>>+      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
>>>>
>>>>"device" here as well?
>>>>
>>>
>>>Same as above.
>>>
>>>>
>>>>>+      flags: [ admin-perm ]
>>>>>+
>>>>>+      do:
>>>>>+        pre: dpll-pre-doit
>>>>>+        post: dpll-post-doit
>>>>>+        request:
>>>>>+          attributes:
>>>>>+            - id
>>>>>+            - bus-name
>>>>>+            - dev-name
>>>>>+            - mode
>>>>
>>>>Hmm, shouldn't source-pin-index be here as well?
>>>
>>>No, there is no set for this.
>>>For manual mode user selects the pin by setting enabled state on the one
>>>he needs to recover signal from.
>>>
>>>source-pin-index is read only, returns active source.
>>
>>Okay, got it. Then why do we have this assymetric approach? Just have
>>the enabled state to serve the user to see which one is selected, no?
>>This would help to avoid confusion (like mine) and allow not to create
>>inconsistencies (like no pin enabled yet driver to return some source
>>pin index)
>>
>
>This is due to automatic mode were multiple pins are enabled, but actual
>selection is done on hardware level with priorities.

Okay, this is confusing and I believe wrong.
You have dual meaning for pin state attribute with states
STATE_CONNECTED/DISCONNECTED:

1) Manual mode, MUX pins (both share the same model):
   There is only one pin with STATE_CONNECTED. The others are in
   STATE_DISCONNECTED
   User changes a state of a pin to make the selection.

   Example:
     $ dplltool pin dump
       pin 1 state connected
       pin 2 state disconnected
     $ dplltool pin 2 set state connected
     $ dplltool pin dump
       pin 1 state disconnected
       pin 2 state connected

2) Automatic mode:
   The user by setting "state" decides it the pin should be considered
   by the device for auto selection.

   Example:
     $ dplltool pin dump:
       pin 1 state connected prio 10
       pin 2 state connected prio 15
     $ dplltool dpll x get:
       dpll x source-pin-index 1

So in manual mode, STATE_CONNECTED means the dpll is connected to this
source pin. However, in automatic mode it means something else. It means
the user allows this pin to be considered for auto selection. The fact
the pin is selected source is exposed over source-pin-index.

Instead of this, I believe that the semantics of
STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
well. Unlike the manual mode/mux, where the state is written by user, in
automatic mode the state should be only written by the driver. User
attemts to set the state should fail with graceful explanation (DPLL
netlink/core code should handle that, w/o driver interaction)

Suggested automatic mode example:
     $ dplltool pin dump:
       pin 1 state connected prio 10 connectable true
       pin 2 state disconnected prio 15 connectable true
     $ dplltool pin 1 set connectable false
     $ dplltool pin dump:
       pin 1 state disconnected prio 10 connectable false
       pin 2 state connected prio 15 connectable true
     $ dplltool pin 1 set state connected
       -EOPNOTSUPP

Note there is no "source-pin-index" at all. Replaced by pin state here.
There is a new attribute called "connectable", the user uses this
attribute to tell the device, if this source pin could be considered for
auto selection or not.

Could be called perhaps "selectable", does not matter. The point is, the
meaning of the "state" attribute is consistent for automatic mode,
manual mode and mux pin.

Makes sense?


>
>[...]
>
>>>>>+
>>>>>+/* 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),
>>>>
>>>>Hmm, any idea why the generator does not put define name
>>>>here instead of "5"?
>>>>
>>>
>>>Not really, it probably needs a fix for this.
>>
>>Yeah.
>>
>
>Well, once we done with review maybe we could also fix those, or ask
>Jakub if he could help :)
>
>
>[...]
>
>>>>
>>>>>+	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,
>>>>
>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>enum name.
>>>
>>>Sure, fixed.
>>
>>
>>Thanks for all your work on this!
>
>Thanks for a great review! :)

Glad to help.

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

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

Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, March 16, 2023 2:45 PM
>>
>
>[...]
>
>>>>>+attribute-sets:
>>>>>+  -
>>>>>+    name: dpll
>>>>>+    enum-name: dplla
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: device
>>>>>+        type: nest
>>>>>+        value: 1
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: device
>>>>
>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>and with the nested scheme looks quite odd.
>>>>
>>>
>>>No, it is nested attribute type, used when multiple devices are returned
>>>with netlink:
>>>
>>>- dump of device-get command where all devices are returned, each one nested
>>>inside it:
>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>>
>>Okay, why is it nested here? The is one netlink msg per dpll device
>>instance. Is this the real output of you made that up?
>>
>>Device nest should not be there for DEVICE_GET, does not make sense.
>>
>
>This was returned by CLI parser on ice with cmd:
>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>--dump device-get
>
>Please note this relates to 'dump' request , it is rather expected that there
>are multiple dplls returned, thus we need a nest attribute for each one.

No, you definitelly don't need to nest them. Dump format and get format
should be exactly the same. Please remove the nest.

See how that is done in devlink for example: devlink_nl_fill()
This functions fills up one object in the dump. No nesting.
I'm not aware of such nesting approach anywhere in kernel dumps, does
not make sense at all.


>
>>
>>>
>>>- do/dump of pin-get, in case of shared pins, each pin contains number of
>>dpll
>>>handles it connects with:
>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>                       'dev-name': '0000:21:00.0_0',
>>>                       'id': 0,
>>>                       'pin-prio': 6,
>>>                       'pin-state': {'doc': 'pin connected',
>>>                                     'name': 'connected'}},
>>>                      {'bus-name': 'pci',
>>>                       'dev-name': '0000:21:00.0_1',
>>>                       'id': 1,
>>>                       'pin-prio': 8,
>>>                       'pin-state': {'doc': 'pin connected',
>>>                                     'name': 'connected'}}],
>>
>>Okay, here I understand it contains device specific pin items. Makes
>>sense!
>>
>
>Good.

Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
reason as above.
I don't see a need for DPLL_A_PIN attr existence, please remove it.




>
>[...]
>
>>>
>>>>
>>>>
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent
>>>>>+        type: nest
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: pin
>>>>>+        value: 23
>>>>
>>>>Value 23? What's this?
>>>>You have it specified for some attrs all over the place.
>>>>What is the reason for it?
>>>>
>>>
>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>I will remove those.
>>>But the others you are refering to (the ones in nested attribute list),
>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>to
>>>prevent the issue?
>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>every "jump" to nested attribute list it is assigning first attribute
>>there
>>>with value=0, thus there is a need to assign a proper value, same as it is
>>on
>>>'main' attribute list.
>>
>>That's weird. Looks like a bug then?
>>
>
>Guess we could call it a bug, I haven't investigated the parser that much,
>AFAIR, other specs are doing the same way.
>
>>
>>>
>>>>
>>>>>+      -
>>>>>+        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: source-pin-idx
>>>>>+        type: u32
>>>>>+      -
>>>>>+        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
>>>>>+        type: nest
>>>>>+        value: 12
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: pin
>>>>
>>>>This does not belong here.
>>>>
>>>
>>>What do you mean?
>>>With device-get 'do' request the list of pins connected to the dpll is
>>>returned, each pin is nested in this attribute.
>>
>>No, wait a sec. You have 2 object types: device and pin. Each have
>>separate netlink CMDs to get and dump individual objects.
>>Don't mix those together like this. I thought it became clear in the
>>past. :/
>>
>
>For pins we must, as pins without a handle to a dpll are pointless.

I'm not talking about per device specific items for pins (state and
prio). That is something else, it's a pin-device tuple. Completely fine.



>Same as a dpll without pins, right?
>
>'do' of DEVICE_GET could just dump it's own status, without the list of pins,

Yes please.


>but it feels easier for handling it's state on userspace counterpart if that
>command also returns currently registered pins. Don't you think so?

No, definitelly not. Please make the object separation clear. Device and
pins are different objects, they have different commands to work with.
Don't mix them together.


>
>>
>>>This is required by parser to work.
>>>
>>>>
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+        value: 21
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-dpll-caps
>>>>>+        type: u32
>>>>>+        value: 26
>>>>
>>>>All these 3 do not belong here are well.
>>>>
>>>
>>>Same as above explanation.
>>
>>Same as above reply.
>>
>>
>>>
>>>>
>>>>
>>>>>+  -
>>>>>+    name: pin
>>>>>+    subset-of: dpll
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: device
>>>>>+        type: nest
>>>>>+        value: 1
>>>>>+        multi-attr: true
>>>>>+        nested-attributes: device
>>>>>+      -
>>>>>+        name: pin-idx
>>>>>+        type: u32
>>>>>+        value: 13
>>>>>+      -
>>>>>+        name: pin-description
>>>>>+        type: string
>>>>>+      -
>>>>>+        name: pin-type
>>>>>+        type: u8
>>>>>+        enum: pin-type
>>>>>+      -
>>>>>+        name: pin-direction
>>>>>+        type: u8
>>>>>+        enum: pin-direction
>>>>>+      -
>>>>>+        name: pin-frequency
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-frequency-supported
>>>>>+        type: u32
>>>>>+        multi-attr: true
>>>>>+      -
>>>>>+        name: pin-any-frequency-min
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-any-frequency-max
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-prio
>>>>>+        type: u32
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent
>>>>>+        type: nest
>>>>>+        multi-attr: true
>>>>
>>>>Multiple parents? How is that supposed to work?
>>>>
>>>
>>>As we have agreed, MUXed pins can have multiple parents.
>>>In our case:
>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>'id': 0},
>>>                     {'bus-name': 'pci',
>>>                      'dev-name': '0000:21:00.0_1',
>>>                      'id': 1}],
>>>          'pin-description': '0000:21:00.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'}}],
>>>          'pin-rclk-device': '0000:21:00.0',
>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>                       'name': 'synce-eth-port'}}]}
>>
>>Got it, it is still a bit hard to me to follow this. Could you
>>perhaps extend the Documentation to describe in more details
>>with examples? Would help a lot for slower people like me to understand
>>what's what.
>>
>
>Actually this is already explained in "MUX-type pins" paragraph of
>Documentation/networking/dpll.rst.
>Do we want to duplicate this explanation here?

No, please extend the docs. As I wrote above, could you add some
examples, like the one you pasted above. Examples always help to
undestand things much better.


>
>
>>
>>>
>>>
>>>>
>>>>>+        nested-attributes: pin-parent
>>>>>+        value: 23
>>>>>+      -
>>>>>+        name: pin-rclk-device
>>>>>+        type: string
>>>>>+        value: 25
>>>>>+      -
>>>>>+        name: pin-dpll-caps
>>>>>+        type: u32
>>>>
>>>>Missing "enum: "
>>>>
>>>
>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>parser won't parse it.
>>
>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>
>
>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>duplicate?

For this, yes. Some small doc note here would be quite convenient.

Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
to do nicer validation as well.


>
>>
>>>
>>>>
>>>>>+  -
>>>>>+    name: pin-parent
>>>>>+    subset-of: dpll
>>>>>+    attributes:
>>>>>+      -
>>>>>+        name: pin-state
>>>>>+        type: u8
>>>>>+        value: 22
>>>>>+        enum: pin-state
>>>>>+      -
>>>>>+        name: pin-parent-idx
>>>>>+        type: u32
>>>>>+        value: 24
>>>>>+      -
>>>>>+        name: pin-rclk-device
>>>>>+        type: string
>>>>
>>>>Yeah, as I wrote in the other email, this really smells to
>>>>have like a simple string like this. What is it supposed to be?
>>>>
>>>
>>>Yes, let's discuss there.
>>
>>Yep.
>>
>>>
>>>>
>>>>>+
>>>>>+
>>>>>+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
>>>>
>>>>Shouldn't this be "device"?
>>>>
>>>
>>>It would brake the parser, again I hope Jakub Kicinski could take a look
>>>on this.
>>
>>Odd.
>>
>
>Yes, seems a bit odd.
>
>>>
>>>>
>>>>>+      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
>>>>
>>>>"device" here as well?
>>>>
>>>
>>>Same as above.
>>>
>>>>
>>>>>+      flags: [ admin-perm ]
>>>>>+
>>>>>+      do:
>>>>>+        pre: dpll-pre-doit
>>>>>+        post: dpll-post-doit
>>>>>+        request:
>>>>>+          attributes:
>>>>>+            - id
>>>>>+            - bus-name
>>>>>+            - dev-name
>>>>>+            - mode
>>>>
>>>>Hmm, shouldn't source-pin-index be here as well?
>>>
>>>No, there is no set for this.
>>>For manual mode user selects the pin by setting enabled state on the one
>>>he needs to recover signal from.
>>>
>>>source-pin-index is read only, returns active source.
>>
>>Okay, got it. Then why do we have this assymetric approach? Just have
>>the enabled state to serve the user to see which one is selected, no?
>>This would help to avoid confusion (like mine) and allow not to create
>>inconsistencies (like no pin enabled yet driver to return some source
>>pin index)
>>
>
>This is due to automatic mode were multiple pins are enabled, but actual
>selection is done on hardware level with priorities.

Okay, this is confusing and I believe wrong.
You have dual meaning for pin state attribute with states
STATE_CONNECTED/DISCONNECTED:

1) Manual mode, MUX pins (both share the same model):
   There is only one pin with STATE_CONNECTED. The others are in
   STATE_DISCONNECTED
   User changes a state of a pin to make the selection.

   Example:
     $ dplltool pin dump
       pin 1 state connected
       pin 2 state disconnected
     $ dplltool pin 2 set state connected
     $ dplltool pin dump
       pin 1 state disconnected
       pin 2 state connected

2) Automatic mode:
   The user by setting "state" decides it the pin should be considered
   by the device for auto selection.

   Example:
     $ dplltool pin dump:
       pin 1 state connected prio 10
       pin 2 state connected prio 15
     $ dplltool dpll x get:
       dpll x source-pin-index 1

So in manual mode, STATE_CONNECTED means the dpll is connected to this
source pin. However, in automatic mode it means something else. It means
the user allows this pin to be considered for auto selection. The fact
the pin is selected source is exposed over source-pin-index.

Instead of this, I believe that the semantics of
STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
well. Unlike the manual mode/mux, where the state is written by user, in
automatic mode the state should be only written by the driver. User
attemts to set the state should fail with graceful explanation (DPLL
netlink/core code should handle that, w/o driver interaction)

Suggested automatic mode example:
     $ dplltool pin dump:
       pin 1 state connected prio 10 connectable true
       pin 2 state disconnected prio 15 connectable true
     $ dplltool pin 1 set connectable false
     $ dplltool pin dump:
       pin 1 state disconnected prio 10 connectable false
       pin 2 state connected prio 15 connectable true
     $ dplltool pin 1 set state connected
       -EOPNOTSUPP

Note there is no "source-pin-index" at all. Replaced by pin state here.
There is a new attribute called "connectable", the user uses this
attribute to tell the device, if this source pin could be considered for
auto selection or not.

Could be called perhaps "selectable", does not matter. The point is, the
meaning of the "state" attribute is consistent for automatic mode,
manual mode and mux pin.

Makes sense?


>
>[...]
>
>>>>>+
>>>>>+/* 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),
>>>>
>>>>Hmm, any idea why the generator does not put define name
>>>>here instead of "5"?
>>>>
>>>
>>>Not really, it probably needs a fix for this.
>>
>>Yeah.
>>
>
>Well, once we done with review maybe we could also fix those, or ask
>Jakub if he could help :)
>
>
>[...]
>
>>>>
>>>>>+	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,
>>>>
>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>enum name.
>>>
>>>Sure, fixed.
>>
>>
>>Thanks for all your work on this!
>
>Thanks for a great review! :)

Glad to help.

_______________________________________________
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] 202+ messages in thread

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

Fri, Mar 17, 2023 at 01:53:49AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, March 16, 2023 4:20 PM
>>
>>Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>>>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>[...]
>>
>>
>>>>>>+      flags: [ admin-perm ]
>>>>>>+
>>>>>>+      do:
>>>>>>+        pre: dpll-pre-doit
>>>>>>+        post: dpll-post-doit
>>>>>>+        request:
>>>>>>+          attributes:
>>>>>>+            - id
>>>>>>+            - bus-name
>>>>>>+            - dev-name
>>>>>>+            - mode
>>>>>
>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>
>>>>No, there is no set for this.
>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>he needs to recover signal from.
>>>>
>>>>source-pin-index is read only, returns active source.
>>>
>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>the enabled state to serve the user to see which one is selected, no?
>>>This would help to avoid confusion (like mine) and allow not to create
>>>inconsistencies (like no pin enabled yet driver to return some source
>>>pin index)
>>
>>Actually, for mlx5 implementation, would be non-trivial to implement
>>this, as each of the pin/port is instantiated and controlled by separate
>>pci backend.
>>
>>Could you please remove, it is not needed and has potential and real
>>issues.
>>
>>[...]
>
>Sorry I cannot, for priority based automatic selection mode multiple sources
>are enabled at any time - selection is done automatically by the chip.
>Thus for that case, this attribute is only way of getting an active source.
>Although, maybe we could allow driver to not implement it, would this help
>for your case? As it seems only required for automatic mode selection.

Please see the other reply for this patch where I describe what I
think is wrong about this approach and suggesting a solution.


>
>Thank you,
>Arkadiusz

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

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

Fri, Mar 17, 2023 at 01:53:49AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, March 16, 2023 4:20 PM
>>
>>Thu, Mar 16, 2023 at 02:45:10PM CET, jiri@resnulli.us wrote:
>>>Thu, Mar 16, 2023 at 02:15:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>[...]
>>
>>
>>>>>>+      flags: [ admin-perm ]
>>>>>>+
>>>>>>+      do:
>>>>>>+        pre: dpll-pre-doit
>>>>>>+        post: dpll-post-doit
>>>>>>+        request:
>>>>>>+          attributes:
>>>>>>+            - id
>>>>>>+            - bus-name
>>>>>>+            - dev-name
>>>>>>+            - mode
>>>>>
>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>
>>>>No, there is no set for this.
>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>he needs to recover signal from.
>>>>
>>>>source-pin-index is read only, returns active source.
>>>
>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>the enabled state to serve the user to see which one is selected, no?
>>>This would help to avoid confusion (like mine) and allow not to create
>>>inconsistencies (like no pin enabled yet driver to return some source
>>>pin index)
>>
>>Actually, for mlx5 implementation, would be non-trivial to implement
>>this, as each of the pin/port is instantiated and controlled by separate
>>pci backend.
>>
>>Could you please remove, it is not needed and has potential and real
>>issues.
>>
>>[...]
>
>Sorry I cannot, for priority based automatic selection mode multiple sources
>are enabled at any time - selection is done automatically by the chip.
>Thus for that case, this attribute is only way of getting an active source.
>Although, maybe we could allow driver to not implement it, would this help
>for your case? As it seems only required for automatic mode selection.

Please see the other reply for this patch where I describe what I
think is wrong about this approach and suggesting a solution.


>
>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] 202+ messages in thread

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

Fri, Mar 17, 2023 at 11:05:26AM CET, jiri@resnulli.us wrote:
>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>
>>
>>[...]
>>
>>>>>>+attribute-sets:
>>>>>>+  -
>>>>>>+    name: dpll
>>>>>>+    enum-name: dplla
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>
>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>and with the nested scheme looks quite odd.
>>>>>
>>>>
>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>with netlink:
>>>>
>>>>- dump of device-get command where all devices are returned, each one nested
>>>>inside it:
>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>>>
>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>instance. Is this the real output of you made that up?
>>>
>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>
>>
>>This was returned by CLI parser on ice with cmd:
>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>--dump device-get
>>
>>Please note this relates to 'dump' request , it is rather expected that there
>>are multiple dplls returned, thus we need a nest attribute for each one.
>
>No, you definitelly don't need to nest them. Dump format and get format
>should be exactly the same. Please remove the nest.

Another example:
$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump dev-get
[{'ifindex': 1, 'xdp-features': set()},
 {'ifindex': 2, 'xdp-features': {'basic', 'rx-sg', 'redirect'}},
 {'ifindex': 3, 'xdp-features': {'basic', 'rx-sg', 'redirect'}},
 {'ifindex': 4, 'xdp-features': set()},
 {'ifindex': 5, 'xdp-features': {'basic', 'rx-sg', 'xsk-zerocopy', 'redirect'}},
 {'ifindex': 6, 'xdp-features': {'basic', 'rx-sg', 'xsk-zerocopy', 'redirect'}}]

[...]

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

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

Fri, Mar 17, 2023 at 11:05:26AM CET, jiri@resnulli.us wrote:
>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>
>>
>>[...]
>>
>>>>>>+attribute-sets:
>>>>>>+  -
>>>>>>+    name: dpll
>>>>>>+    enum-name: dplla
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>
>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>and with the nested scheme looks quite odd.
>>>>>
>>>>
>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>with netlink:
>>>>
>>>>- dump of device-get command where all devices are returned, each one nested
>>>>inside it:
>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id': 1}]}]
>>>
>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>instance. Is this the real output of you made that up?
>>>
>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>
>>
>>This was returned by CLI parser on ice with cmd:
>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>--dump device-get
>>
>>Please note this relates to 'dump' request , it is rather expected that there
>>are multiple dplls returned, thus we need a nest attribute for each one.
>
>No, you definitelly don't need to nest them. Dump format and get format
>should be exactly the same. Please remove the nest.

Another example:
$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump dev-get
[{'ifindex': 1, 'xdp-features': set()},
 {'ifindex': 2, 'xdp-features': {'basic', 'rx-sg', 'redirect'}},
 {'ifindex': 3, 'xdp-features': {'basic', 'rx-sg', 'redirect'}},
 {'ifindex': 4, 'xdp-features': set()},
 {'ifindex': 5, 'xdp-features': {'basic', 'rx-sg', 'xsk-zerocopy', 'redirect'}},
 {'ifindex': 6, 'xdp-features': {'basic', 'rx-sg', 'xsk-zerocopy', 'redirect'}}]

[...]

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-17 10:05             ` Jiri Pirko
@ 2023-03-17 15:14               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 15:14 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 11:05 AM
>
>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>
>>
>>[...]
>>
>>>>>>+attribute-sets:
>>>>>>+  -
>>>>>>+    name: dpll
>>>>>>+    enum-name: dplla
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>
>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>and with the nested scheme looks quite odd.
>>>>>
>>>>
>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>with netlink:
>>>>
>>>>- dump of device-get command where all devices are returned, each one
>>>>nested
>>>>inside it:
>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':
>>>>1}]}]
>>>
>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>instance. Is this the real output of you made that up?
>>>
>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>
>>
>>This was returned by CLI parser on ice with cmd:
>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>--dump device-get
>>
>>Please note this relates to 'dump' request , it is rather expected that
>there
>>are multiple dplls returned, thus we need a nest attribute for each one.
>
>No, you definitelly don't need to nest them. Dump format and get format
>should be exactly the same. Please remove the nest.
>
>See how that is done in devlink for example: devlink_nl_fill()
>This functions fills up one object in the dump. No nesting.
>I'm not aware of such nesting approach anywhere in kernel dumps, does
>not make sense at all.
>

Yeah it make sense to have same output on `do` and `dump`, but this is also
achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
on `do` only one.

Will try to fix it.
Although could you please explain why it make sense to put extra header
(exactly the same header) multiple times in one netlink response message? 

>
>>
>>>
>>>>
>>>>- do/dump of pin-get, in case of shared pins, each pin contains number
>of
>>>dpll
>>>>handles it connects with:
>>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>>                       'dev-name': '0000:21:00.0_0',
>>>>                       'id': 0,
>>>>                       'pin-prio': 6,
>>>>                       'pin-state': {'doc': 'pin connected',
>>>>                                     'name': 'connected'}},
>>>>                      {'bus-name': 'pci',
>>>>                       'dev-name': '0000:21:00.0_1',
>>>>                       'id': 1,
>>>>                       'pin-prio': 8,
>>>>                       'pin-state': {'doc': 'pin connected',
>>>>                                     'name': 'connected'}}],
>>>
>>>Okay, here I understand it contains device specific pin items. Makes
>>>sense!
>>>
>>
>>Good.
>
>Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
>reason as above.
>I don't see a need for DPLL_A_PIN attr existence, please remove it.
>
>

Sure, will try.

>
>
>>
>>[...]
>>
>>>>
>>>>>
>>>>>
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-parent
>>>>>>+        type: nest
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: pin
>>>>>>+        value: 23
>>>>>
>>>>>Value 23? What's this?
>>>>>You have it specified for some attrs all over the place.
>>>>>What is the reason for it?
>>>>>
>>>>
>>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>>I will remove those.
>>>>But the others you are refering to (the ones in nested attribute list),
>>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>>to
>>>>prevent the issue?
>>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>>every "jump" to nested attribute list it is assigning first attribute
>>>there
>>>>with value=0, thus there is a need to assign a proper value, same as it is
>>>on
>>>>'main' attribute list.
>>>
>>>That's weird. Looks like a bug then?
>>>
>>
>>Guess we could call it a bug, I haven't investigated the parser that much,
>>AFAIR, other specs are doing the same way.
>>
>>>
>>>>
>>>>>
>>>>>>+      -
>>>>>>+        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: source-pin-idx
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        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
>>>>>>+        type: nest
>>>>>>+        value: 12
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: pin
>>>>>
>>>>>This does not belong here.
>>>>>
>>>>
>>>>What do you mean?
>>>>With device-get 'do' request the list of pins connected to the dpll is
>>>>returned, each pin is nested in this attribute.
>>>
>>>No, wait a sec. You have 2 object types: device and pin. Each have
>>>separate netlink CMDs to get and dump individual objects.
>>>Don't mix those together like this. I thought it became clear in the
>>>past. :/
>>>
>>
>>For pins we must, as pins without a handle to a dpll are pointless.
>
>I'm not talking about per device specific items for pins (state and
>prio). That is something else, it's a pin-device tuple. Completely fine.
>
>
>
>>Same as a dpll without pins, right?
>>
>>'do' of DEVICE_GET could just dump it's own status, without the list of
>pins,
>
>Yes please.
>
>
>>but it feels easier for handling it's state on userspace counterpart if
>>that command also returns currently registered pins. Don't you think so?
>
>No, definitelly not. Please make the object separation clear. Device and
>pins are different objects, they have different commands to work with.
>Don't mix them together.
>

It does, since the user app wouldn't have to dump/get pins continuously.
But yeah, as object separation argument makes sense will try to fix it.

>
>>
>>>
>>>>This is required by parser to work.
>>>>
>>>>>
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+        value: 21
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-dpll-caps
>>>>>>+        type: u32
>>>>>>+        value: 26
>>>>>
>>>>>All these 3 do not belong here are well.
>>>>>
>>>>
>>>>Same as above explanation.
>>>
>>>Same as above reply.
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>+  -
>>>>>>+    name: pin
>>>>>>+    subset-of: dpll
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>>+      -
>>>>>>+        name: pin-idx
>>>>>>+        type: u32
>>>>>>+        value: 13
>>>>>>+      -
>>>>>>+        name: pin-description
>>>>>>+        type: string
>>>>>>+      -
>>>>>>+        name: pin-type
>>>>>>+        type: u8
>>>>>>+        enum: pin-type
>>>>>>+      -
>>>>>>+        name: pin-direction
>>>>>>+        type: u8
>>>>>>+        enum: pin-direction
>>>>>>+      -
>>>>>>+        name: pin-frequency
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-frequency-supported
>>>>>>+        type: u32
>>>>>>+        multi-attr: true
>>>>>>+      -
>>>>>>+        name: pin-any-frequency-min
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-any-frequency-max
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-parent
>>>>>>+        type: nest
>>>>>>+        multi-attr: true
>>>>>
>>>>>Multiple parents? How is that supposed to work?
>>>>>
>>>>
>>>>As we have agreed, MUXed pins can have multiple parents.
>>>>In our case:
>>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>>'id': 0},
>>>>                     {'bus-name': 'pci',
>>>>                      'dev-name': '0000:21:00.0_1',
>>>>                      'id': 1}],
>>>>          'pin-description': '0000:21:00.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'}}],
>>>>          'pin-rclk-device': '0000:21:00.0',
>>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>>                       'name': 'synce-eth-port'}}]}
>>>
>>>Got it, it is still a bit hard to me to follow this. Could you
>>>perhaps extend the Documentation to describe in more details
>>>with examples? Would help a lot for slower people like me to understand
>>>what's what.
>>>
>>
>>Actually this is already explained in "MUX-type pins" paragraph of
>>Documentation/networking/dpll.rst.
>>Do we want to duplicate this explanation here?
>
>No, please extend the docs. As I wrote above, could you add some
>examples, like the one you pasted above. Examples always help to
>undestand things much better.
>

Sure, fixed.

>
>>
>>
>>>
>>>>
>>>>
>>>>>
>>>>>>+        nested-attributes: pin-parent
>>>>>>+        value: 23
>>>>>>+      -
>>>>>>+        name: pin-rclk-device
>>>>>>+        type: string
>>>>>>+        value: 25
>>>>>>+      -
>>>>>>+        name: pin-dpll-caps
>>>>>>+        type: u32
>>>>>
>>>>>Missing "enum: "
>>>>>
>>>>
>>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>>parser won't parse it.
>>>
>>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>>
>>
>>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>>duplicate?
>
>For this, yes. Some small doc note here would be quite convenient.
>
>Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
>use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
>to do nicer validation as well.
>

Actually BITFIELD32 is to much for this case, the bit itself would be enough
as we don't need validity bit.
But yeah, as there is no BITMASK yet, will try to change to BITFIELD32.

[...]

>>>>>
>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>
>>>>No, there is no set for this.
>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>he needs to recover signal from.
>>>>
>>>>source-pin-index is read only, returns active source.
>>>
>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>the enabled state to serve the user to see which one is selected, no?
>>>This would help to avoid confusion (like mine) and allow not to create
>>>inconsistencies (like no pin enabled yet driver to return some source
>>>pin index)
>>>
>>
>>This is due to automatic mode were multiple pins are enabled, but actual
>>selection is done on hardware level with priorities.
>
>Okay, this is confusing and I believe wrong.
>You have dual meaning for pin state attribute with states
>STATE_CONNECTED/DISCONNECTED:
>
>1) Manual mode, MUX pins (both share the same model):
>   There is only one pin with STATE_CONNECTED. The others are in
>   STATE_DISCONNECTED
>   User changes a state of a pin to make the selection.
>
>   Example:
>     $ dplltool pin dump
>       pin 1 state connected
>       pin 2 state disconnected
>     $ dplltool pin 2 set state connected
>     $ dplltool pin dump
>       pin 1 state disconnected
>       pin 2 state connected
>
>2) Automatic mode:
>   The user by setting "state" decides it the pin should be considered
>   by the device for auto selection.
>
>   Example:
>     $ dplltool pin dump:
>       pin 1 state connected prio 10
>       pin 2 state connected prio 15
>     $ dplltool dpll x get:
>       dpll x source-pin-index 1
>
>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>source pin. However, in automatic mode it means something else. It means
>the user allows this pin to be considered for auto selection. The fact
>the pin is selected source is exposed over source-pin-index.
>
>Instead of this, I believe that the semantics of
>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>well. Unlike the manual mode/mux, where the state is written by user, in
>automatic mode the state should be only written by the driver. User
>attemts to set the state should fail with graceful explanation (DPLL
>netlink/core code should handle that, w/o driver interaction)
>
>Suggested automatic mode example:
>     $ dplltool pin dump:
>       pin 1 state connected prio 10 connectable true
>       pin 2 state disconnected prio 15 connectable true
>     $ dplltool pin 1 set connectable false
>     $ dplltool pin dump:
>       pin 1 state disconnected prio 10 connectable false
>       pin 2 state connected prio 15 connectable true
>     $ dplltool pin 1 set state connected
>       -EOPNOTSUPP
>
>Note there is no "source-pin-index" at all. Replaced by pin state here.
>There is a new attribute called "connectable", the user uses this
>attribute to tell the device, if this source pin could be considered for
>auto selection or not.
>
>Could be called perhaps "selectable", does not matter. The point is, the
>meaning of the "state" attribute is consistent for automatic mode,
>manual mode and mux pin.
>
>Makes sense?
>

Great idea!
I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
In the end we will have this:
              +--------------------------------+
              | valid DPLL_A_PIN_STATE values  |
	      +---------------+----------------+
+------------+| requested:    | returned:      |
|DPLL_A_MODE:||               |                |
|------------++--------------------------------|
|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
|            ||- DISCONNECTED | - DISCONNECTED |
|            ||               | - CONNECTED    |
|------------++--------------------------------|
|MANUAL      ||- CONNECTED    | - CONNECTED    |
|            ||- DISCONNECTED | - DISCONNECTED |
+------------++---------------+----------------+

Thank you,
Arkadiusz

>
>>
>>[...]
>>
>>>>>>+
>>>>>>+/* 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),
>>>>>
>>>>>Hmm, any idea why the generator does not put define name
>>>>>here instead of "5"?
>>>>>
>>>>
>>>>Not really, it probably needs a fix for this.
>>>
>>>Yeah.
>>>
>>
>>Well, once we done with review maybe we could also fix those, or ask
>>Jakub if he could help :)
>>
>>
>>[...]
>>
>>>>>
>>>>>>+	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,
>>>>>
>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>enum name.
>>>>
>>>>Sure, fixed.
>>>
>>>
>>>Thanks for all your work on this!
>>
>>Thanks for a great review! :)
>
>Glad to help.

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

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17 15:14               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 15:14 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 11:05 AM
>
>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>
>>
>>[...]
>>
>>>>>>+attribute-sets:
>>>>>>+  -
>>>>>>+    name: dpll
>>>>>>+    enum-name: dplla
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>
>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>and with the nested scheme looks quite odd.
>>>>>
>>>>
>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>with netlink:
>>>>
>>>>- dump of device-get command where all devices are returned, each one
>>>>nested
>>>>inside it:
>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':
>>>>1}]}]
>>>
>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>instance. Is this the real output of you made that up?
>>>
>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>
>>
>>This was returned by CLI parser on ice with cmd:
>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>--dump device-get
>>
>>Please note this relates to 'dump' request , it is rather expected that
>there
>>are multiple dplls returned, thus we need a nest attribute for each one.
>
>No, you definitelly don't need to nest them. Dump format and get format
>should be exactly the same. Please remove the nest.
>
>See how that is done in devlink for example: devlink_nl_fill()
>This functions fills up one object in the dump. No nesting.
>I'm not aware of such nesting approach anywhere in kernel dumps, does
>not make sense at all.
>

Yeah it make sense to have same output on `do` and `dump`, but this is also
achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
on `do` only one.

Will try to fix it.
Although could you please explain why it make sense to put extra header
(exactly the same header) multiple times in one netlink response message? 

>
>>
>>>
>>>>
>>>>- do/dump of pin-get, in case of shared pins, each pin contains number
>of
>>>dpll
>>>>handles it connects with:
>>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>>                       'dev-name': '0000:21:00.0_0',
>>>>                       'id': 0,
>>>>                       'pin-prio': 6,
>>>>                       'pin-state': {'doc': 'pin connected',
>>>>                                     'name': 'connected'}},
>>>>                      {'bus-name': 'pci',
>>>>                       'dev-name': '0000:21:00.0_1',
>>>>                       'id': 1,
>>>>                       'pin-prio': 8,
>>>>                       'pin-state': {'doc': 'pin connected',
>>>>                                     'name': 'connected'}}],
>>>
>>>Okay, here I understand it contains device specific pin items. Makes
>>>sense!
>>>
>>
>>Good.
>
>Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
>reason as above.
>I don't see a need for DPLL_A_PIN attr existence, please remove it.
>
>

Sure, will try.

>
>
>>
>>[...]
>>
>>>>
>>>>>
>>>>>
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-parent
>>>>>>+        type: nest
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: pin
>>>>>>+        value: 23
>>>>>
>>>>>Value 23? What's this?
>>>>>You have it specified for some attrs all over the place.
>>>>>What is the reason for it?
>>>>>
>>>>
>>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>>I will remove those.
>>>>But the others you are refering to (the ones in nested attribute list),
>>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>>to
>>>>prevent the issue?
>>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>>every "jump" to nested attribute list it is assigning first attribute
>>>there
>>>>with value=0, thus there is a need to assign a proper value, same as it is
>>>on
>>>>'main' attribute list.
>>>
>>>That's weird. Looks like a bug then?
>>>
>>
>>Guess we could call it a bug, I haven't investigated the parser that much,
>>AFAIR, other specs are doing the same way.
>>
>>>
>>>>
>>>>>
>>>>>>+      -
>>>>>>+        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: source-pin-idx
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        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
>>>>>>+        type: nest
>>>>>>+        value: 12
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: pin
>>>>>
>>>>>This does not belong here.
>>>>>
>>>>
>>>>What do you mean?
>>>>With device-get 'do' request the list of pins connected to the dpll is
>>>>returned, each pin is nested in this attribute.
>>>
>>>No, wait a sec. You have 2 object types: device and pin. Each have
>>>separate netlink CMDs to get and dump individual objects.
>>>Don't mix those together like this. I thought it became clear in the
>>>past. :/
>>>
>>
>>For pins we must, as pins without a handle to a dpll are pointless.
>
>I'm not talking about per device specific items for pins (state and
>prio). That is something else, it's a pin-device tuple. Completely fine.
>
>
>
>>Same as a dpll without pins, right?
>>
>>'do' of DEVICE_GET could just dump it's own status, without the list of
>pins,
>
>Yes please.
>
>
>>but it feels easier for handling it's state on userspace counterpart if
>>that command also returns currently registered pins. Don't you think so?
>
>No, definitelly not. Please make the object separation clear. Device and
>pins are different objects, they have different commands to work with.
>Don't mix them together.
>

It does, since the user app wouldn't have to dump/get pins continuously.
But yeah, as object separation argument makes sense will try to fix it.

>
>>
>>>
>>>>This is required by parser to work.
>>>>
>>>>>
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+        value: 21
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-dpll-caps
>>>>>>+        type: u32
>>>>>>+        value: 26
>>>>>
>>>>>All these 3 do not belong here are well.
>>>>>
>>>>
>>>>Same as above explanation.
>>>
>>>Same as above reply.
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>+  -
>>>>>>+    name: pin
>>>>>>+    subset-of: dpll
>>>>>>+    attributes:
>>>>>>+      -
>>>>>>+        name: device
>>>>>>+        type: nest
>>>>>>+        value: 1
>>>>>>+        multi-attr: true
>>>>>>+        nested-attributes: device
>>>>>>+      -
>>>>>>+        name: pin-idx
>>>>>>+        type: u32
>>>>>>+        value: 13
>>>>>>+      -
>>>>>>+        name: pin-description
>>>>>>+        type: string
>>>>>>+      -
>>>>>>+        name: pin-type
>>>>>>+        type: u8
>>>>>>+        enum: pin-type
>>>>>>+      -
>>>>>>+        name: pin-direction
>>>>>>+        type: u8
>>>>>>+        enum: pin-direction
>>>>>>+      -
>>>>>>+        name: pin-frequency
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-frequency-supported
>>>>>>+        type: u32
>>>>>>+        multi-attr: true
>>>>>>+      -
>>>>>>+        name: pin-any-frequency-min
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-any-frequency-max
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-prio
>>>>>>+        type: u32
>>>>>>+      -
>>>>>>+        name: pin-state
>>>>>>+        type: u8
>>>>>>+        enum: pin-state
>>>>>>+      -
>>>>>>+        name: pin-parent
>>>>>>+        type: nest
>>>>>>+        multi-attr: true
>>>>>
>>>>>Multiple parents? How is that supposed to work?
>>>>>
>>>>
>>>>As we have agreed, MUXed pins can have multiple parents.
>>>>In our case:
>>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>>'id': 0},
>>>>                     {'bus-name': 'pci',
>>>>                      'dev-name': '0000:21:00.0_1',
>>>>                      'id': 1}],
>>>>          'pin-description': '0000:21:00.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'}}],
>>>>          'pin-rclk-device': '0000:21:00.0',
>>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>>                       'name': 'synce-eth-port'}}]}
>>>
>>>Got it, it is still a bit hard to me to follow this. Could you
>>>perhaps extend the Documentation to describe in more details
>>>with examples? Would help a lot for slower people like me to understand
>>>what's what.
>>>
>>
>>Actually this is already explained in "MUX-type pins" paragraph of
>>Documentation/networking/dpll.rst.
>>Do we want to duplicate this explanation here?
>
>No, please extend the docs. As I wrote above, could you add some
>examples, like the one you pasted above. Examples always help to
>undestand things much better.
>

Sure, fixed.

>
>>
>>
>>>
>>>>
>>>>
>>>>>
>>>>>>+        nested-attributes: pin-parent
>>>>>>+        value: 23
>>>>>>+      -
>>>>>>+        name: pin-rclk-device
>>>>>>+        type: string
>>>>>>+        value: 25
>>>>>>+      -
>>>>>>+        name: pin-dpll-caps
>>>>>>+        type: u32
>>>>>
>>>>>Missing "enum: "
>>>>>
>>>>
>>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>>parser won't parse it.
>>>
>>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>>
>>
>>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>>duplicate?
>
>For this, yes. Some small doc note here would be quite convenient.
>
>Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
>use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
>to do nicer validation as well.
>

Actually BITFIELD32 is to much for this case, the bit itself would be enough
as we don't need validity bit.
But yeah, as there is no BITMASK yet, will try to change to BITFIELD32.

[...]

>>>>>
>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>
>>>>No, there is no set for this.
>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>he needs to recover signal from.
>>>>
>>>>source-pin-index is read only, returns active source.
>>>
>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>the enabled state to serve the user to see which one is selected, no?
>>>This would help to avoid confusion (like mine) and allow not to create
>>>inconsistencies (like no pin enabled yet driver to return some source
>>>pin index)
>>>
>>
>>This is due to automatic mode were multiple pins are enabled, but actual
>>selection is done on hardware level with priorities.
>
>Okay, this is confusing and I believe wrong.
>You have dual meaning for pin state attribute with states
>STATE_CONNECTED/DISCONNECTED:
>
>1) Manual mode, MUX pins (both share the same model):
>   There is only one pin with STATE_CONNECTED. The others are in
>   STATE_DISCONNECTED
>   User changes a state of a pin to make the selection.
>
>   Example:
>     $ dplltool pin dump
>       pin 1 state connected
>       pin 2 state disconnected
>     $ dplltool pin 2 set state connected
>     $ dplltool pin dump
>       pin 1 state disconnected
>       pin 2 state connected
>
>2) Automatic mode:
>   The user by setting "state" decides it the pin should be considered
>   by the device for auto selection.
>
>   Example:
>     $ dplltool pin dump:
>       pin 1 state connected prio 10
>       pin 2 state connected prio 15
>     $ dplltool dpll x get:
>       dpll x source-pin-index 1
>
>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>source pin. However, in automatic mode it means something else. It means
>the user allows this pin to be considered for auto selection. The fact
>the pin is selected source is exposed over source-pin-index.
>
>Instead of this, I believe that the semantics of
>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>well. Unlike the manual mode/mux, where the state is written by user, in
>automatic mode the state should be only written by the driver. User
>attemts to set the state should fail with graceful explanation (DPLL
>netlink/core code should handle that, w/o driver interaction)
>
>Suggested automatic mode example:
>     $ dplltool pin dump:
>       pin 1 state connected prio 10 connectable true
>       pin 2 state disconnected prio 15 connectable true
>     $ dplltool pin 1 set connectable false
>     $ dplltool pin dump:
>       pin 1 state disconnected prio 10 connectable false
>       pin 2 state connected prio 15 connectable true
>     $ dplltool pin 1 set state connected
>       -EOPNOTSUPP
>
>Note there is no "source-pin-index" at all. Replaced by pin state here.
>There is a new attribute called "connectable", the user uses this
>attribute to tell the device, if this source pin could be considered for
>auto selection or not.
>
>Could be called perhaps "selectable", does not matter. The point is, the
>meaning of the "state" attribute is consistent for automatic mode,
>manual mode and mux pin.
>
>Makes sense?
>

Great idea!
I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
In the end we will have this:
              +--------------------------------+
              | valid DPLL_A_PIN_STATE values  |
	      +---------------+----------------+
+------------+| requested:    | returned:      |
|DPLL_A_MODE:||               |                |
|------------++--------------------------------|
|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
|            ||- DISCONNECTED | - DISCONNECTED |
|            ||               | - CONNECTED    |
|------------++--------------------------------|
|MANUAL      ||- CONNECTED    | - CONNECTED    |
|            ||- DISCONNECTED | - DISCONNECTED |
+------------++---------------+----------------+

Thank you,
Arkadiusz

>
>>
>>[...]
>>
>>>>>>+
>>>>>>+/* 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),
>>>>>
>>>>>Hmm, any idea why the generator does not put define name
>>>>>here instead of "5"?
>>>>>
>>>>
>>>>Not really, it probably needs a fix for this.
>>>
>>>Yeah.
>>>
>>
>>Well, once we done with review maybe we could also fix those, or ask
>>Jakub if he could help :)
>>
>>
>>[...]
>>
>>>>>
>>>>>>+	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,
>>>>>
>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>enum name.
>>>>
>>>>Sure, fixed.
>>>
>>>
>>>Thanks for all your work on this!
>>
>>Thanks for a great review! :)
>
>Glad to help.

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-13 15:33     ` Vadim Fedorenko
@ 2023-03-17 16:10       ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:10 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>On 13/03/2023 12:20, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> > Implement common API for clock/DPLL configuration and status reporting.
>> > The API utilises netlink interface as transport for commands and event
>> > notifications. This API aim to extend current pin configuration and
>> > make it flexible and easy to cover special configurations.
>> 
>> Could you please put here some command line examples to work with this?
>
>We don't have open-source tools ready right now for specific hardware, but
>with YAML spec published you can use in-kernel tool to manipulate the values,
>i.e.:
>
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>device-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>device-get --json '{"id": 0}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>pin-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-get --json '{"id": 0, "pin-idx":1}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

Interesting, is this working for you?
I'm experiencing some issue with cmd value. Example:
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-get --json '{"id": 0}'
this should send DPLL_CMD_DEVICE_GET which is 1.
In kernel genl_family_rcv_msg() the hdr->cmd is 2
Any idea what might be wrong?


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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-17 16:10       ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:10 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Mon, Mar 13, 2023 at 04:33:13PM CET, vadim.fedorenko@linux.dev wrote:
>On 13/03/2023 12:20, Jiri Pirko wrote:
>> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> > Implement common API for clock/DPLL configuration and status reporting.
>> > The API utilises netlink interface as transport for commands and event
>> > notifications. This API aim to extend current pin configuration and
>> > make it flexible and easy to cover special configurations.
>> 
>> Could you please put here some command line examples to work with this?
>
>We don't have open-source tools ready right now for specific hardware, but
>with YAML spec published you can use in-kernel tool to manipulate the values,
>i.e.:
>
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>device-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>device-get --json '{"id": 0}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --dump
>pin-get
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-get --json '{"id": 0, "pin-idx":1}'
>./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>pin-set --json '{"id":0, "pin-idx":1, "pin-frequency":1}'

Interesting, is this working for you?
I'm experiencing some issue with cmd value. Example:
./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-get --json '{"id": 0}'
this should send DPLL_CMD_DEVICE_GET which is 1.
In kernel genl_family_rcv_msg() the hdr->cmd is 2
Any idea what might be wrong?


_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-17 15:14               ` Kubalewski, Arkadiusz
@ 2023-03-17 16:20                 ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:20 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, March 17, 2023 11:05 AM
>>
>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>
>>>
>>>[...]
>>>
>>>>>>>+attribute-sets:
>>>>>>>+  -
>>>>>>>+    name: dpll
>>>>>>>+    enum-name: dplla
>>>>>>>+    attributes:
>>>>>>>+      -
>>>>>>>+        name: device
>>>>>>>+        type: nest
>>>>>>>+        value: 1
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: device
>>>>>>
>>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>>and with the nested scheme looks quite odd.
>>>>>>
>>>>>
>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>with netlink:
>>>>>
>>>>>- dump of device-get command where all devices are returned, each one
>>>>>nested
>>>>>inside it:
>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':
>>>>>1}]}]
>>>>
>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>instance. Is this the real output of you made that up?
>>>>
>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>
>>>
>>>This was returned by CLI parser on ice with cmd:
>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>--dump device-get
>>>
>>>Please note this relates to 'dump' request , it is rather expected that
>>there
>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>
>>No, you definitelly don't need to nest them. Dump format and get format
>>should be exactly the same. Please remove the nest.
>>
>>See how that is done in devlink for example: devlink_nl_fill()
>>This functions fills up one object in the dump. No nesting.
>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>not make sense at all.
>>
>
>Yeah it make sense to have same output on `do` and `dump`, but this is also
>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>on `do` only one.

Please don't. This root nesting is not correct.


>
>Will try to fix it.
>Although could you please explain why it make sense to put extra header
>(exactly the same header) multiple times in one netlink response message? 

This is how it's done for all netlink dumps as far as I know.
The reason might be that the userspace is parsing exactly the same
message as if it would be DOIT message.


>
>>
>>>
>>>>
>>>>>
>>>>>- do/dump of pin-get, in case of shared pins, each pin contains number
>>of
>>>>dpll
>>>>>handles it connects with:
>>>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>>>                       'dev-name': '0000:21:00.0_0',
>>>>>                       'id': 0,
>>>>>                       'pin-prio': 6,
>>>>>                       'pin-state': {'doc': 'pin connected',
>>>>>                                     'name': 'connected'}},
>>>>>                      {'bus-name': 'pci',
>>>>>                       'dev-name': '0000:21:00.0_1',
>>>>>                       'id': 1,
>>>>>                       'pin-prio': 8,
>>>>>                       'pin-state': {'doc': 'pin connected',
>>>>>                                     'name': 'connected'}}],
>>>>
>>>>Okay, here I understand it contains device specific pin items. Makes
>>>>sense!
>>>>
>>>
>>>Good.
>>
>>Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
>>reason as above.
>>I don't see a need for DPLL_A_PIN attr existence, please remove it.
>>
>>
>
>Sure, will try.

Cool.


>
>>
>>
>>>
>>>[...]
>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-parent
>>>>>>>+        type: nest
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: pin
>>>>>>>+        value: 23
>>>>>>
>>>>>>Value 23? What's this?
>>>>>>You have it specified for some attrs all over the place.
>>>>>>What is the reason for it?
>>>>>>
>>>>>
>>>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>>>I will remove those.
>>>>>But the others you are refering to (the ones in nested attribute list),
>>>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>>>to
>>>>>prevent the issue?
>>>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>>>every "jump" to nested attribute list it is assigning first attribute
>>>>there
>>>>>with value=0, thus there is a need to assign a proper value, same as it is
>>>>on
>>>>>'main' attribute list.
>>>>
>>>>That's weird. Looks like a bug then?
>>>>
>>>
>>>Guess we could call it a bug, I haven't investigated the parser that much,
>>>AFAIR, other specs are doing the same way.
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        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: source-pin-idx
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        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
>>>>>>>+        type: nest
>>>>>>>+        value: 12
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: pin
>>>>>>
>>>>>>This does not belong here.
>>>>>>
>>>>>
>>>>>What do you mean?
>>>>>With device-get 'do' request the list of pins connected to the dpll is
>>>>>returned, each pin is nested in this attribute.
>>>>
>>>>No, wait a sec. You have 2 object types: device and pin. Each have
>>>>separate netlink CMDs to get and dump individual objects.
>>>>Don't mix those together like this. I thought it became clear in the
>>>>past. :/
>>>>
>>>
>>>For pins we must, as pins without a handle to a dpll are pointless.
>>
>>I'm not talking about per device specific items for pins (state and
>>prio). That is something else, it's a pin-device tuple. Completely fine.
>>
>>
>>
>>>Same as a dpll without pins, right?
>>>
>>>'do' of DEVICE_GET could just dump it's own status, without the list of
>>pins,
>>
>>Yes please.
>>
>>
>>>but it feels easier for handling it's state on userspace counterpart if
>>>that command also returns currently registered pins. Don't you think so?
>>
>>No, definitelly not. Please make the object separation clear. Device and
>>pins are different objects, they have different commands to work with.
>>Don't mix them together.
>>
>
>It does, since the user app wouldn't have to dump/get pins continuously.

I don't follow. For every pin change there is going to be pin object
dumped over monitoring netlink.


>But yeah, as object separation argument makes sense will try to fix it.

Awesome.


>
>>
>>>
>>>>
>>>>>This is required by parser to work.
>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+        value: 21
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-dpll-caps
>>>>>>>+        type: u32
>>>>>>>+        value: 26
>>>>>>
>>>>>>All these 3 do not belong here are well.
>>>>>>
>>>>>
>>>>>Same as above explanation.
>>>>
>>>>Same as above reply.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+  -
>>>>>>>+    name: pin
>>>>>>>+    subset-of: dpll
>>>>>>>+    attributes:
>>>>>>>+      -
>>>>>>>+        name: device
>>>>>>>+        type: nest
>>>>>>>+        value: 1
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: device
>>>>>>>+      -
>>>>>>>+        name: pin-idx
>>>>>>>+        type: u32
>>>>>>>+        value: 13
>>>>>>>+      -
>>>>>>>+        name: pin-description
>>>>>>>+        type: string
>>>>>>>+      -
>>>>>>>+        name: pin-type
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-type
>>>>>>>+      -
>>>>>>>+        name: pin-direction
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-direction
>>>>>>>+      -
>>>>>>>+        name: pin-frequency
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-frequency-supported
>>>>>>>+        type: u32
>>>>>>>+        multi-attr: true
>>>>>>>+      -
>>>>>>>+        name: pin-any-frequency-min
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-any-frequency-max
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-parent
>>>>>>>+        type: nest
>>>>>>>+        multi-attr: true
>>>>>>
>>>>>>Multiple parents? How is that supposed to work?
>>>>>>
>>>>>
>>>>>As we have agreed, MUXed pins can have multiple parents.
>>>>>In our case:
>>>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>>>'id': 0},
>>>>>                     {'bus-name': 'pci',
>>>>>                      'dev-name': '0000:21:00.0_1',
>>>>>                      'id': 1}],
>>>>>          'pin-description': '0000:21:00.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'}}],
>>>>>          'pin-rclk-device': '0000:21:00.0',
>>>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>>>                       'name': 'synce-eth-port'}}]}
>>>>
>>>>Got it, it is still a bit hard to me to follow this. Could you
>>>>perhaps extend the Documentation to describe in more details
>>>>with examples? Would help a lot for slower people like me to understand
>>>>what's what.
>>>>
>>>
>>>Actually this is already explained in "MUX-type pins" paragraph of
>>>Documentation/networking/dpll.rst.
>>>Do we want to duplicate this explanation here?
>>
>>No, please extend the docs. As I wrote above, could you add some
>>examples, like the one you pasted above. Examples always help to
>>undestand things much better.
>>
>
>Sure, fixed.
>
>>
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>+        nested-attributes: pin-parent
>>>>>>>+        value: 23
>>>>>>>+      -
>>>>>>>+        name: pin-rclk-device
>>>>>>>+        type: string
>>>>>>>+        value: 25
>>>>>>>+      -
>>>>>>>+        name: pin-dpll-caps
>>>>>>>+        type: u32
>>>>>>
>>>>>>Missing "enum: "
>>>>>>
>>>>>
>>>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>>>parser won't parse it.
>>>>
>>>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>>>
>>>
>>>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>>>duplicate?
>>
>>For this, yes. Some small doc note here would be quite convenient.
>>
>>Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
>>use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
>>to do nicer validation as well.
>>
>
>Actually BITFIELD32 is to much for this case, the bit itself would be enough
>as we don't need validity bit.
>But yeah, as there is no BITMASK yet, will try to change to BITFIELD32.
>
>[...]
>
>>>>>>
>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>
>>>>>No, there is no set for this.
>>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>>he needs to recover signal from.
>>>>>
>>>>>source-pin-index is read only, returns active source.
>>>>
>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>the enabled state to serve the user to see which one is selected, no?
>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>pin index)
>>>>
>>>
>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>selection is done on hardware level with priorities.
>>
>>Okay, this is confusing and I believe wrong.
>>You have dual meaning for pin state attribute with states
>>STATE_CONNECTED/DISCONNECTED:
>>
>>1) Manual mode, MUX pins (both share the same model):
>>   There is only one pin with STATE_CONNECTED. The others are in
>>   STATE_DISCONNECTED
>>   User changes a state of a pin to make the selection.
>>
>>   Example:
>>     $ dplltool pin dump
>>       pin 1 state connected
>>       pin 2 state disconnected
>>     $ dplltool pin 2 set state connected
>>     $ dplltool pin dump
>>       pin 1 state disconnected
>>       pin 2 state connected
>>
>>2) Automatic mode:
>>   The user by setting "state" decides it the pin should be considered
>>   by the device for auto selection.
>>
>>   Example:
>>     $ dplltool pin dump:
>>       pin 1 state connected prio 10
>>       pin 2 state connected prio 15
>>     $ dplltool dpll x get:
>>       dpll x source-pin-index 1
>>
>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>source pin. However, in automatic mode it means something else. It means
>>the user allows this pin to be considered for auto selection. The fact
>>the pin is selected source is exposed over source-pin-index.
>>
>>Instead of this, I believe that the semantics of
>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>well. Unlike the manual mode/mux, where the state is written by user, in
>>automatic mode the state should be only written by the driver. User
>>attemts to set the state should fail with graceful explanation (DPLL
>>netlink/core code should handle that, w/o driver interaction)
>>
>>Suggested automatic mode example:
>>     $ dplltool pin dump:
>>       pin 1 state connected prio 10 connectable true
>>       pin 2 state disconnected prio 15 connectable true
>>     $ dplltool pin 1 set connectable false
>>     $ dplltool pin dump:
>>       pin 1 state disconnected prio 10 connectable false
>>       pin 2 state connected prio 15 connectable true
>>     $ dplltool pin 1 set state connected
>>       -EOPNOTSUPP
>>
>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>There is a new attribute called "connectable", the user uses this
>>attribute to tell the device, if this source pin could be considered for
>>auto selection or not.
>>
>>Could be called perhaps "selectable", does not matter. The point is, the
>>meaning of the "state" attribute is consistent for automatic mode,
>>manual mode and mux pin.
>>
>>Makes sense?
>>
>
>Great idea!
>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>In the end we will have this:
>              +--------------------------------+
>              | valid DPLL_A_PIN_STATE values  |
>	      +---------------+----------------+
>+------------+| requested:    | returned:      |
>|DPLL_A_MODE:||               |                |
>|------------++--------------------------------|
>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>|            ||- DISCONNECTED | - DISCONNECTED |
>|            ||               | - CONNECTED    |

"selectable" is something the user sets.
"connected"/"disconnected" is in case of auto mode something that driver
sets.

Looks a bit odd to mix them together. That is why I suggested
to have sepectable as a separate attr. But up to you. Please make sure
you sanitize the user/driver set of this attr in dpll code.


>|------------++--------------------------------|
>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>|            ||- DISCONNECTED | - DISCONNECTED |
>+------------++---------------+----------------+
>
>Thank you,
>Arkadiusz
>
>>
>>>
>>>[...]
>>>
>>>>>>>+
>>>>>>>+/* 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),
>>>>>>
>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>here instead of "5"?
>>>>>>
>>>>>
>>>>>Not really, it probably needs a fix for this.
>>>>
>>>>Yeah.
>>>>
>>>
>>>Well, once we done with review maybe we could also fix those, or ask
>>>Jakub if he could help :)
>>>
>>>
>>>[...]
>>>
>>>>>>
>>>>>>>+	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,
>>>>>>
>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>enum name.
>>>>>
>>>>>Sure, fixed.
>>>>
>>>>
>>>>Thanks for all your work on this!
>>>
>>>Thanks for a great review! :)
>>
>>Glad to help.

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

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

Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, March 17, 2023 11:05 AM
>>
>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>
>>>
>>>[...]
>>>
>>>>>>>+attribute-sets:
>>>>>>>+  -
>>>>>>>+    name: dpll
>>>>>>>+    enum-name: dplla
>>>>>>>+    attributes:
>>>>>>>+      -
>>>>>>>+        name: device
>>>>>>>+        type: nest
>>>>>>>+        value: 1
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: device
>>>>>>
>>>>>>What is this "device" and what is it good for? Smells like some leftover
>>>>>>and with the nested scheme looks quite odd.
>>>>>>
>>>>>
>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>with netlink:
>>>>>
>>>>>- dump of device-get command where all devices are returned, each one
>>>>>nested
>>>>>inside it:
>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id': 0},
>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':
>>>>>1}]}]
>>>>
>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>instance. Is this the real output of you made that up?
>>>>
>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>
>>>
>>>This was returned by CLI parser on ice with cmd:
>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>--dump device-get
>>>
>>>Please note this relates to 'dump' request , it is rather expected that
>>there
>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>
>>No, you definitelly don't need to nest them. Dump format and get format
>>should be exactly the same. Please remove the nest.
>>
>>See how that is done in devlink for example: devlink_nl_fill()
>>This functions fills up one object in the dump. No nesting.
>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>not make sense at all.
>>
>
>Yeah it make sense to have same output on `do` and `dump`, but this is also
>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>on `do` only one.

Please don't. This root nesting is not correct.


>
>Will try to fix it.
>Although could you please explain why it make sense to put extra header
>(exactly the same header) multiple times in one netlink response message? 

This is how it's done for all netlink dumps as far as I know.
The reason might be that the userspace is parsing exactly the same
message as if it would be DOIT message.


>
>>
>>>
>>>>
>>>>>
>>>>>- do/dump of pin-get, in case of shared pins, each pin contains number
>>of
>>>>dpll
>>>>>handles it connects with:
>>>>>[{'pin': [{'device': [{'bus-name': 'pci',
>>>>>                       'dev-name': '0000:21:00.0_0',
>>>>>                       'id': 0,
>>>>>                       'pin-prio': 6,
>>>>>                       'pin-state': {'doc': 'pin connected',
>>>>>                                     'name': 'connected'}},
>>>>>                      {'bus-name': 'pci',
>>>>>                       'dev-name': '0000:21:00.0_1',
>>>>>                       'id': 1,
>>>>>                       'pin-prio': 8,
>>>>>                       'pin-state': {'doc': 'pin connected',
>>>>>                                     'name': 'connected'}}],
>>>>
>>>>Okay, here I understand it contains device specific pin items. Makes
>>>>sense!
>>>>
>>>
>>>Good.
>>
>>Make sure you don't nest the pin objects for dump (DPLL_A_PIN). Same
>>reason as above.
>>I don't see a need for DPLL_A_PIN attr existence, please remove it.
>>
>>
>
>Sure, will try.

Cool.


>
>>
>>
>>>
>>>[...]
>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-parent
>>>>>>>+        type: nest
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: pin
>>>>>>>+        value: 23
>>>>>>
>>>>>>Value 23? What's this?
>>>>>>You have it specified for some attrs all over the place.
>>>>>>What is the reason for it?
>>>>>>
>>>>>
>>>>>Actually this particular one is not needed (also value: 12 on pin above),
>>>>>I will remove those.
>>>>>But the others you are refering to (the ones in nested attribute list),
>>>>>are required because of cli.py parser issue, maybe Kuba knows a better way
>>>>to
>>>>>prevent the issue?
>>>>>Basically, without those values, cli.py brakes on parsing responses, after
>>>>>every "jump" to nested attribute list it is assigning first attribute
>>>>there
>>>>>with value=0, thus there is a need to assign a proper value, same as it is
>>>>on
>>>>>'main' attribute list.
>>>>
>>>>That's weird. Looks like a bug then?
>>>>
>>>
>>>Guess we could call it a bug, I haven't investigated the parser that much,
>>>AFAIR, other specs are doing the same way.
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        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: source-pin-idx
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        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
>>>>>>>+        type: nest
>>>>>>>+        value: 12
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: pin
>>>>>>
>>>>>>This does not belong here.
>>>>>>
>>>>>
>>>>>What do you mean?
>>>>>With device-get 'do' request the list of pins connected to the dpll is
>>>>>returned, each pin is nested in this attribute.
>>>>
>>>>No, wait a sec. You have 2 object types: device and pin. Each have
>>>>separate netlink CMDs to get and dump individual objects.
>>>>Don't mix those together like this. I thought it became clear in the
>>>>past. :/
>>>>
>>>
>>>For pins we must, as pins without a handle to a dpll are pointless.
>>
>>I'm not talking about per device specific items for pins (state and
>>prio). That is something else, it's a pin-device tuple. Completely fine.
>>
>>
>>
>>>Same as a dpll without pins, right?
>>>
>>>'do' of DEVICE_GET could just dump it's own status, without the list of
>>pins,
>>
>>Yes please.
>>
>>
>>>but it feels easier for handling it's state on userspace counterpart if
>>>that command also returns currently registered pins. Don't you think so?
>>
>>No, definitelly not. Please make the object separation clear. Device and
>>pins are different objects, they have different commands to work with.
>>Don't mix them together.
>>
>
>It does, since the user app wouldn't have to dump/get pins continuously.

I don't follow. For every pin change there is going to be pin object
dumped over monitoring netlink.


>But yeah, as object separation argument makes sense will try to fix it.

Awesome.


>
>>
>>>
>>>>
>>>>>This is required by parser to work.
>>>>>
>>>>>>
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+        value: 21
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-dpll-caps
>>>>>>>+        type: u32
>>>>>>>+        value: 26
>>>>>>
>>>>>>All these 3 do not belong here are well.
>>>>>>
>>>>>
>>>>>Same as above explanation.
>>>>
>>>>Same as above reply.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+  -
>>>>>>>+    name: pin
>>>>>>>+    subset-of: dpll
>>>>>>>+    attributes:
>>>>>>>+      -
>>>>>>>+        name: device
>>>>>>>+        type: nest
>>>>>>>+        value: 1
>>>>>>>+        multi-attr: true
>>>>>>>+        nested-attributes: device
>>>>>>>+      -
>>>>>>>+        name: pin-idx
>>>>>>>+        type: u32
>>>>>>>+        value: 13
>>>>>>>+      -
>>>>>>>+        name: pin-description
>>>>>>>+        type: string
>>>>>>>+      -
>>>>>>>+        name: pin-type
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-type
>>>>>>>+      -
>>>>>>>+        name: pin-direction
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-direction
>>>>>>>+      -
>>>>>>>+        name: pin-frequency
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-frequency-supported
>>>>>>>+        type: u32
>>>>>>>+        multi-attr: true
>>>>>>>+      -
>>>>>>>+        name: pin-any-frequency-min
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-any-frequency-max
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-prio
>>>>>>>+        type: u32
>>>>>>>+      -
>>>>>>>+        name: pin-state
>>>>>>>+        type: u8
>>>>>>>+        enum: pin-state
>>>>>>>+      -
>>>>>>>+        name: pin-parent
>>>>>>>+        type: nest
>>>>>>>+        multi-attr: true
>>>>>>
>>>>>>Multiple parents? How is that supposed to work?
>>>>>>
>>>>>
>>>>>As we have agreed, MUXed pins can have multiple parents.
>>>>>In our case:
>>>>>/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do
>>>>>pin-get --json '{"id": 0, "pin-idx":13}'
>>>>>{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
>>>>>'id': 0},
>>>>>                     {'bus-name': 'pci',
>>>>>                      'dev-name': '0000:21:00.0_1',
>>>>>                      'id': 1}],
>>>>>          'pin-description': '0000:21:00.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'}}],
>>>>>          'pin-rclk-device': '0000:21:00.0',
>>>>>          'pin-type': {'doc': "ethernet port PHY's recovered clock",
>>>>>                       'name': 'synce-eth-port'}}]}
>>>>
>>>>Got it, it is still a bit hard to me to follow this. Could you
>>>>perhaps extend the Documentation to describe in more details
>>>>with examples? Would help a lot for slower people like me to understand
>>>>what's what.
>>>>
>>>
>>>Actually this is already explained in "MUX-type pins" paragraph of
>>>Documentation/networking/dpll.rst.
>>>Do we want to duplicate this explanation here?
>>
>>No, please extend the docs. As I wrote above, could you add some
>>examples, like the one you pasted above. Examples always help to
>>undestand things much better.
>>
>
>Sure, fixed.
>
>>
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>+        nested-attributes: pin-parent
>>>>>>>+        value: 23
>>>>>>>+      -
>>>>>>>+        name: pin-rclk-device
>>>>>>>+        type: string
>>>>>>>+        value: 25
>>>>>>>+      -
>>>>>>>+        name: pin-dpll-caps
>>>>>>>+        type: u32
>>>>>>
>>>>>>Missing "enum: "
>>>>>>
>>>>>
>>>>>It is actually a bitmask, this is why didn't set as enum, with enum type
>>>>>parser won't parse it.
>>>>
>>>>Ah! Got it. Perhaps a docs note with the enum pointer then?
>>>>
>>>
>>>Same as above, explained in Documentation/networking/dpll.rst, do wan't to
>>>duplicate?
>>
>>For this, yes. Some small doc note here would be quite convenient.
>>
>>Also, I almost forgot: Please don't use NLA_U32 for caps flags. Please
>>use NLA_BITFIELD32 which was introduced for exactly this purpose. Allows
>>to do nicer validation as well.
>>
>
>Actually BITFIELD32 is to much for this case, the bit itself would be enough
>as we don't need validity bit.
>But yeah, as there is no BITMASK yet, will try to change to BITFIELD32.
>
>[...]
>
>>>>>>
>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>
>>>>>No, there is no set for this.
>>>>>For manual mode user selects the pin by setting enabled state on the one
>>>>>he needs to recover signal from.
>>>>>
>>>>>source-pin-index is read only, returns active source.
>>>>
>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>the enabled state to serve the user to see which one is selected, no?
>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>pin index)
>>>>
>>>
>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>selection is done on hardware level with priorities.
>>
>>Okay, this is confusing and I believe wrong.
>>You have dual meaning for pin state attribute with states
>>STATE_CONNECTED/DISCONNECTED:
>>
>>1) Manual mode, MUX pins (both share the same model):
>>   There is only one pin with STATE_CONNECTED. The others are in
>>   STATE_DISCONNECTED
>>   User changes a state of a pin to make the selection.
>>
>>   Example:
>>     $ dplltool pin dump
>>       pin 1 state connected
>>       pin 2 state disconnected
>>     $ dplltool pin 2 set state connected
>>     $ dplltool pin dump
>>       pin 1 state disconnected
>>       pin 2 state connected
>>
>>2) Automatic mode:
>>   The user by setting "state" decides it the pin should be considered
>>   by the device for auto selection.
>>
>>   Example:
>>     $ dplltool pin dump:
>>       pin 1 state connected prio 10
>>       pin 2 state connected prio 15
>>     $ dplltool dpll x get:
>>       dpll x source-pin-index 1
>>
>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>source pin. However, in automatic mode it means something else. It means
>>the user allows this pin to be considered for auto selection. The fact
>>the pin is selected source is exposed over source-pin-index.
>>
>>Instead of this, I believe that the semantics of
>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>well. Unlike the manual mode/mux, where the state is written by user, in
>>automatic mode the state should be only written by the driver. User
>>attemts to set the state should fail with graceful explanation (DPLL
>>netlink/core code should handle that, w/o driver interaction)
>>
>>Suggested automatic mode example:
>>     $ dplltool pin dump:
>>       pin 1 state connected prio 10 connectable true
>>       pin 2 state disconnected prio 15 connectable true
>>     $ dplltool pin 1 set connectable false
>>     $ dplltool pin dump:
>>       pin 1 state disconnected prio 10 connectable false
>>       pin 2 state connected prio 15 connectable true
>>     $ dplltool pin 1 set state connected
>>       -EOPNOTSUPP
>>
>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>There is a new attribute called "connectable", the user uses this
>>attribute to tell the device, if this source pin could be considered for
>>auto selection or not.
>>
>>Could be called perhaps "selectable", does not matter. The point is, the
>>meaning of the "state" attribute is consistent for automatic mode,
>>manual mode and mux pin.
>>
>>Makes sense?
>>
>
>Great idea!
>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>In the end we will have this:
>              +--------------------------------+
>              | valid DPLL_A_PIN_STATE values  |
>	      +---------------+----------------+
>+------------+| requested:    | returned:      |
>|DPLL_A_MODE:||               |                |
>|------------++--------------------------------|
>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>|            ||- DISCONNECTED | - DISCONNECTED |
>|            ||               | - CONNECTED    |

"selectable" is something the user sets.
"connected"/"disconnected" is in case of auto mode something that driver
sets.

Looks a bit odd to mix them together. That is why I suggested
to have sepectable as a separate attr. But up to you. Please make sure
you sanitize the user/driver set of this attr in dpll code.


>|------------++--------------------------------|
>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>|            ||- DISCONNECTED | - DISCONNECTED |
>+------------++---------------+----------------+
>
>Thank you,
>Arkadiusz
>
>>
>>>
>>>[...]
>>>
>>>>>>>+
>>>>>>>+/* 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),
>>>>>>
>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>here instead of "5"?
>>>>>>
>>>>>
>>>>>Not really, it probably needs a fix for this.
>>>>
>>>>Yeah.
>>>>
>>>
>>>Well, once we done with review maybe we could also fix those, or ask
>>>Jakub if he could help :)
>>>
>>>
>>>[...]
>>>
>>>>>>
>>>>>>>+	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,
>>>>>>
>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>enum name.
>>>>>
>>>>>Sure, fixed.
>>>>
>>>>
>>>>Thanks for all your work on this!
>>>
>>>Thanks for a great review! :)
>>
>>Glad to help.

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_device *dpll;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
>+		ret = dpll_msg_add_dev_handle(skb, dpll);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}

Please fillup the attrs for the object. The format should be exactly the
same as for doit.


>+		nla_nest_end(skb, nest);
>+	}
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	return ret;
>+}
>+

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct nlattr *hdr, *nest;
>+	struct dpll_device *dpll;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+		nest = nla_nest_start(skb, DPLL_A_DEVICE);
>+		ret = dpll_msg_add_dev_handle(skb, dpll);
>+		if (ret) {
>+			nla_nest_cancel(skb, nest);
>+			break;
>+		}

Please fillup the attrs for the object. The format should be exactly the
same as for doit.


>+		nla_nest_end(skb, nest);
>+	}
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-17 16:23     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:23 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]

>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[6] = {

It's odd to see 6 here. Should by just []
But it's an issue of Jakub's generator.

[...]

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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17 16:23     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:23 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]

>+/* Ops table for dpll */
>+static const struct genl_split_ops dpll_nl_ops[6] = {

It's odd to see 6 here. Should by just []
But it's an issue of Jakub's generator.

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-12  2:28   ` Vadim Fedorenko
@ 2023-03-17 16:53     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:53 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>

[...]


>+      name: device-get
>+      doc: |
>+        Get list of DPLL devices (dump) or attributes of a single dpll device
>+      attribute-set: dpll
>+      flags: [ admin-perm ]
>+

[...]


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

Any particular reason to have admin cap required for get operations?
If not, please remove.


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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17 16:53     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-17 16:53 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>

[...]


>+      name: device-get
>+      doc: |
>+        Get list of DPLL devices (dump) or attributes of a single dpll device
>+      attribute-set: dpll
>+      flags: [ admin-perm ]
>+

[...]


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

Any particular reason to have admin cap required for get operations?
If not, please remove.


_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-17 16:20                 ` Jiri Pirko
@ 2023-03-17 18:22                   ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 18:22 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 5:21 PM
>
>Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, March 17, 2023 11:05 AM
>>>
>>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>>
>>>>
>>>>[...]
>>>>
>>>>>>>>+attribute-sets:
>>>>>>>>+  -
>>>>>>>>+    name: dpll
>>>>>>>>+    enum-name: dplla
>>>>>>>>+    attributes:
>>>>>>>>+      -
>>>>>>>>+        name: device
>>>>>>>>+        type: nest
>>>>>>>>+        value: 1
>>>>>>>>+        multi-attr: true
>>>>>>>>+        nested-attributes: device
>>>>>>>
>>>>>>>What is this "device" and what is it good for? Smells like some
>>>>>>>leftover
>>>>>>>and with the nested scheme looks quite odd.
>>>>>>>
>>>>>>
>>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>>with netlink:
>>>>>>
>>>>>>- dump of device-get command where all devices are returned, each one
>>>>>>nested
>>>>>>inside it:
>>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id':0},
>>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':1}]}]
>>>>>
>>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>>instance. Is this the real output of you made that up?
>>>>>
>>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>>
>>>>
>>>>This was returned by CLI parser on ice with cmd:
>>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>>--dump device-get
>>>>
>>>>Please note this relates to 'dump' request , it is rather expected that
>>>there
>>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>>
>>>No, you definitelly don't need to nest them. Dump format and get format
>>>should be exactly the same. Please remove the nest.
>>>
>>>See how that is done in devlink for example: devlink_nl_fill()
>>>This functions fills up one object in the dump. No nesting.
>>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>>not make sense at all.
>>>
>>
>>Yeah it make sense to have same output on `do` and `dump`, but this is also
>>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>>on `do` only one.
>
>Please don't. This root nesting is not correct.
>
>
>>
>>Will try to fix it.
>>Although could you please explain why it make sense to put extra header
>>(exactly the same header) multiple times in one netlink response message?
>
>This is how it's done for all netlink dumps as far as I know.

So we just following something but we cannot explain why?

>The reason might be that the userspace is parsing exactly the same
>message as if it would be DOIT message.
>

This argument is achievable on both approaches.

[...]


>>>>>>>
>>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>>
>>>>>>No, there is no set for this.
>>>>>>For manual mode user selects the pin by setting enabled state on the
>one
>>>>>>he needs to recover signal from.
>>>>>>
>>>>>>source-pin-index is read only, returns active source.
>>>>>
>>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>>the enabled state to serve the user to see which one is selected, no?
>>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>>pin index)
>>>>>
>>>>
>>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>>selection is done on hardware level with priorities.
>>>
>>>Okay, this is confusing and I believe wrong.
>>>You have dual meaning for pin state attribute with states
>>>STATE_CONNECTED/DISCONNECTED:
>>>
>>>1) Manual mode, MUX pins (both share the same model):
>>>   There is only one pin with STATE_CONNECTED. The others are in
>>>   STATE_DISCONNECTED
>>>   User changes a state of a pin to make the selection.
>>>
>>>   Example:
>>>     $ dplltool pin dump
>>>       pin 1 state connected
>>>       pin 2 state disconnected
>>>     $ dplltool pin 2 set state connected
>>>     $ dplltool pin dump
>>>       pin 1 state disconnected
>>>       pin 2 state connected
>>>
>>>2) Automatic mode:
>>>   The user by setting "state" decides it the pin should be considered
>>>   by the device for auto selection.
>>>
>>>   Example:
>>>     $ dplltool pin dump:
>>>       pin 1 state connected prio 10
>>>       pin 2 state connected prio 15
>>>     $ dplltool dpll x get:
>>>       dpll x source-pin-index 1
>>>
>>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>>source pin. However, in automatic mode it means something else. It means
>>>the user allows this pin to be considered for auto selection. The fact
>>>the pin is selected source is exposed over source-pin-index.
>>>
>>>Instead of this, I believe that the semantics of
>>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>>well. Unlike the manual mode/mux, where the state is written by user, in
>>>automatic mode the state should be only written by the driver. User
>>>attemts to set the state should fail with graceful explanation (DPLL
>>>netlink/core code should handle that, w/o driver interaction)
>>>
>>>Suggested automatic mode example:
>>>     $ dplltool pin dump:
>>>       pin 1 state connected prio 10 connectable true
>>>       pin 2 state disconnected prio 15 connectable true
>>>     $ dplltool pin 1 set connectable false
>>>     $ dplltool pin dump:
>>>       pin 1 state disconnected prio 10 connectable false
>>>       pin 2 state connected prio 15 connectable true
>>>     $ dplltool pin 1 set state connected
>>>       -EOPNOTSUPP
>>>
>>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>>There is a new attribute called "connectable", the user uses this
>>>attribute to tell the device, if this source pin could be considered for
>>>auto selection or not.
>>>
>>>Could be called perhaps "selectable", does not matter. The point is, the
>>>meaning of the "state" attribute is consistent for automatic mode,
>>>manual mode and mux pin.
>>>
>>>Makes sense?
>>>
>>
>>Great idea!
>>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>>In the end we will have this:
>>              +--------------------------------+
>>              | valid DPLL_A_PIN_STATE values  |
>>	      +---------------+----------------+
>>+------------+| requested:    | returned:      |
>>|DPLL_A_MODE:||               |                |
>>|------------++--------------------------------|
>>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>>|            ||- DISCONNECTED | - DISCONNECTED |
>>|            ||               | - CONNECTED    |
>
>"selectable" is something the user sets.

Yes.

>"connected"/"disconnected" is in case of auto mode something that driver
>sets.
>

No. Not really.
"CONNECTED" is only set by driver once a pin is choosen.
"SELECTABLE" is set by the user if he needs to enable a pin for selection,
it is also default state of a pin if it was not selected ("CONNECTED")
"DISCONNECTED" is set by the user if he needs to disable a pin from selection.

>Looks a bit odd to mix them together. That is why I suggested
>to have sepectable as a separate attr. But up to you. Please make sure
>you sanitize the user/driver set of this attr in dpll code.
>

What is odd?
What do you mean by "sanitize the user/driver set of this attr in dpll code"?


Thank you,
Arkadiusz

>
>>|------------++--------------------------------|
>>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>>|            ||- DISCONNECTED | - DISCONNECTED |
>>+------------++---------------+----------------+
>>
>>Thank you,
>>Arkadiusz
>>
>>>
>>>>
>>>>[...]
>>>>
>>>>>>>>+
>>>>>>>>+/* 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),
>>>>>>>
>>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>>here instead of "5"?
>>>>>>>
>>>>>>
>>>>>>Not really, it probably needs a fix for this.
>>>>>
>>>>>Yeah.
>>>>>
>>>>
>>>>Well, once we done with review maybe we could also fix those, or ask
>>>>Jakub if he could help :)
>>>>
>>>>
>>>>[...]
>>>>
>>>>>>>
>>>>>>>>+	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,
>>>>>>>
>>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>>enum name.
>>>>>>
>>>>>>Sure, fixed.
>>>>>
>>>>>
>>>>>Thanks for all your work on this!
>>>>
>>>>Thanks for a great review! :)
>>>
>>>Glad to help.

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

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17 18:22                   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 18:22 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 5:21 PM
>
>Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, March 17, 2023 11:05 AM
>>>
>>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>>
>>>>
>>>>[...]
>>>>
>>>>>>>>+attribute-sets:
>>>>>>>>+  -
>>>>>>>>+    name: dpll
>>>>>>>>+    enum-name: dplla
>>>>>>>>+    attributes:
>>>>>>>>+      -
>>>>>>>>+        name: device
>>>>>>>>+        type: nest
>>>>>>>>+        value: 1
>>>>>>>>+        multi-attr: true
>>>>>>>>+        nested-attributes: device
>>>>>>>
>>>>>>>What is this "device" and what is it good for? Smells like some
>>>>>>>leftover
>>>>>>>and with the nested scheme looks quite odd.
>>>>>>>
>>>>>>
>>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>>with netlink:
>>>>>>
>>>>>>- dump of device-get command where all devices are returned, each one
>>>>>>nested
>>>>>>inside it:
>>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id':0},
>>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':1}]}]
>>>>>
>>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>>instance. Is this the real output of you made that up?
>>>>>
>>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>>
>>>>
>>>>This was returned by CLI parser on ice with cmd:
>>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>>--dump device-get
>>>>
>>>>Please note this relates to 'dump' request , it is rather expected that
>>>there
>>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>>
>>>No, you definitelly don't need to nest them. Dump format and get format
>>>should be exactly the same. Please remove the nest.
>>>
>>>See how that is done in devlink for example: devlink_nl_fill()
>>>This functions fills up one object in the dump. No nesting.
>>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>>not make sense at all.
>>>
>>
>>Yeah it make sense to have same output on `do` and `dump`, but this is also
>>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>>on `do` only one.
>
>Please don't. This root nesting is not correct.
>
>
>>
>>Will try to fix it.
>>Although could you please explain why it make sense to put extra header
>>(exactly the same header) multiple times in one netlink response message?
>
>This is how it's done for all netlink dumps as far as I know.

So we just following something but we cannot explain why?

>The reason might be that the userspace is parsing exactly the same
>message as if it would be DOIT message.
>

This argument is achievable on both approaches.

[...]


>>>>>>>
>>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>>
>>>>>>No, there is no set for this.
>>>>>>For manual mode user selects the pin by setting enabled state on the
>one
>>>>>>he needs to recover signal from.
>>>>>>
>>>>>>source-pin-index is read only, returns active source.
>>>>>
>>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>>the enabled state to serve the user to see which one is selected, no?
>>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>>pin index)
>>>>>
>>>>
>>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>>selection is done on hardware level with priorities.
>>>
>>>Okay, this is confusing and I believe wrong.
>>>You have dual meaning for pin state attribute with states
>>>STATE_CONNECTED/DISCONNECTED:
>>>
>>>1) Manual mode, MUX pins (both share the same model):
>>>   There is only one pin with STATE_CONNECTED. The others are in
>>>   STATE_DISCONNECTED
>>>   User changes a state of a pin to make the selection.
>>>
>>>   Example:
>>>     $ dplltool pin dump
>>>       pin 1 state connected
>>>       pin 2 state disconnected
>>>     $ dplltool pin 2 set state connected
>>>     $ dplltool pin dump
>>>       pin 1 state disconnected
>>>       pin 2 state connected
>>>
>>>2) Automatic mode:
>>>   The user by setting "state" decides it the pin should be considered
>>>   by the device for auto selection.
>>>
>>>   Example:
>>>     $ dplltool pin dump:
>>>       pin 1 state connected prio 10
>>>       pin 2 state connected prio 15
>>>     $ dplltool dpll x get:
>>>       dpll x source-pin-index 1
>>>
>>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>>source pin. However, in automatic mode it means something else. It means
>>>the user allows this pin to be considered for auto selection. The fact
>>>the pin is selected source is exposed over source-pin-index.
>>>
>>>Instead of this, I believe that the semantics of
>>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>>well. Unlike the manual mode/mux, where the state is written by user, in
>>>automatic mode the state should be only written by the driver. User
>>>attemts to set the state should fail with graceful explanation (DPLL
>>>netlink/core code should handle that, w/o driver interaction)
>>>
>>>Suggested automatic mode example:
>>>     $ dplltool pin dump:
>>>       pin 1 state connected prio 10 connectable true
>>>       pin 2 state disconnected prio 15 connectable true
>>>     $ dplltool pin 1 set connectable false
>>>     $ dplltool pin dump:
>>>       pin 1 state disconnected prio 10 connectable false
>>>       pin 2 state connected prio 15 connectable true
>>>     $ dplltool pin 1 set state connected
>>>       -EOPNOTSUPP
>>>
>>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>>There is a new attribute called "connectable", the user uses this
>>>attribute to tell the device, if this source pin could be considered for
>>>auto selection or not.
>>>
>>>Could be called perhaps "selectable", does not matter. The point is, the
>>>meaning of the "state" attribute is consistent for automatic mode,
>>>manual mode and mux pin.
>>>
>>>Makes sense?
>>>
>>
>>Great idea!
>>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>>In the end we will have this:
>>              +--------------------------------+
>>              | valid DPLL_A_PIN_STATE values  |
>>	      +---------------+----------------+
>>+------------+| requested:    | returned:      |
>>|DPLL_A_MODE:||               |                |
>>|------------++--------------------------------|
>>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>>|            ||- DISCONNECTED | - DISCONNECTED |
>>|            ||               | - CONNECTED    |
>
>"selectable" is something the user sets.

Yes.

>"connected"/"disconnected" is in case of auto mode something that driver
>sets.
>

No. Not really.
"CONNECTED" is only set by driver once a pin is choosen.
"SELECTABLE" is set by the user if he needs to enable a pin for selection,
it is also default state of a pin if it was not selected ("CONNECTED")
"DISCONNECTED" is set by the user if he needs to disable a pin from selection.

>Looks a bit odd to mix them together. That is why I suggested
>to have sepectable as a separate attr. But up to you. Please make sure
>you sanitize the user/driver set of this attr in dpll code.
>

What is odd?
What do you mean by "sanitize the user/driver set of this attr in dpll code"?


Thank you,
Arkadiusz

>
>>|------------++--------------------------------|
>>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>>|            ||- DISCONNECTED | - DISCONNECTED |
>>+------------++---------------+----------------+
>>
>>Thank you,
>>Arkadiusz
>>
>>>
>>>>
>>>>[...]
>>>>
>>>>>>>>+
>>>>>>>>+/* 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),
>>>>>>>
>>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>>here instead of "5"?
>>>>>>>
>>>>>>
>>>>>>Not really, it probably needs a fix for this.
>>>>>
>>>>>Yeah.
>>>>>
>>>>
>>>>Well, once we done with review maybe we could also fix those, or ask
>>>>Jakub if he could help :)
>>>>
>>>>
>>>>[...]
>>>>
>>>>>>>
>>>>>>>>+	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,
>>>>>>>
>>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>>enum name.
>>>>>>
>>>>>>Sure, fixed.
>>>>>
>>>>>
>>>>>Thanks for all your work on this!
>>>>
>>>>Thanks for a great review! :)
>>>
>>>Glad to help.

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-17 16:53     ` Jiri Pirko
@ 2023-03-17 18:50       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 18:50 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 5:54 PM
>
>Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>
>[...]
>
>
>>+      name: device-get
>>+      doc: |
>>+        Get list of DPLL devices (dump) or attributes of a single dpll
>>device
>>+      attribute-set: dpll
>>+      flags: [ admin-perm ]
>>+
>
>[...]
>
>
>>+    -
>>+      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 ]
>
>Any particular reason to have admin cap required for get operations?
>If not, please remove.

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.

Thank you,
Arkadiusz

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

* RE: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
@ 2023-03-17 18:50       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-17 18:50 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, March 17, 2023 5:54 PM
>
>Sun, Mar 12, 2023 at 03:28:02AM CET, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>
>[...]
>
>
>>+      name: device-get
>>+      doc: |
>>+        Get list of DPLL devices (dump) or attributes of a single dpll
>>device
>>+      attribute-set: dpll
>>+      flags: [ admin-perm ]
>>+
>
>[...]
>
>
>>+    -
>>+      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 ]
>
>Any particular reason to have admin cap required for get operations?
>If not, please remove.

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.

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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-17 16:10       ` Jiri Pirko
@ 2023-03-18  5:01         ` Jakub Kicinski
  -1 siblings, 0 replies; 202+ messages in thread
From: Jakub Kicinski @ 2023-03-18  5:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Vadim Fedorenko, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On Fri, 17 Mar 2023 17:10:04 +0100 Jiri Pirko wrote:
> Interesting, is this working for you?
> I'm experiencing some issue with cmd value. Example:
> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-get --json '{"id": 0}'
> this should send DPLL_CMD_DEVICE_GET which is 1.
> In kernel genl_family_rcv_msg() the hdr->cmd is 2
> Any idea what might be wrong?

I changed the default value for the first element of an attribute set
from 0 to 1 to avoid having to add all those (pointless?)

+      -
+        name: unspec
+        doc: unspecified value

at the start. So delete those or explicitly set them to value: 0.

See commit ad4fafcde5bc ("tools: ynl: use 1 as the default for first
entry in attrs/ops").

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-18  5:01         ` Jakub Kicinski
  0 siblings, 0 replies; 202+ messages in thread
From: Jakub Kicinski @ 2023-03-18  5:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Vadim Fedorenko, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On Fri, 17 Mar 2023 17:10:04 +0100 Jiri Pirko wrote:
> Interesting, is this working for you?
> I'm experiencing some issue with cmd value. Example:
> ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-get --json '{"id": 0}'
> this should send DPLL_CMD_DEVICE_GET which is 1.
> In kernel genl_family_rcv_msg() the hdr->cmd is 2
> Any idea what might be wrong?

I changed the default value for the first element of an attribute set
from 0 to 1 to avoid having to add all those (pointless?)

+      -
+        name: unspec
+        doc: unspecified value

at the start. So delete those or explicitly set them to value: 0.

See commit ad4fafcde5bc ("tools: ynl: use 1 as the default for first
entry in attrs/ops").

_______________________________________________
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] 202+ messages in thread

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

Fri, Mar 17, 2023 at 07:22:46PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, March 17, 2023 5:21 PM
>>
>>Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, March 17, 2023 11:05 AM
>>>>
>>>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>>+attribute-sets:
>>>>>>>>>+  -
>>>>>>>>>+    name: dpll
>>>>>>>>>+    enum-name: dplla
>>>>>>>>>+    attributes:
>>>>>>>>>+      -
>>>>>>>>>+        name: device
>>>>>>>>>+        type: nest
>>>>>>>>>+        value: 1
>>>>>>>>>+        multi-attr: true
>>>>>>>>>+        nested-attributes: device
>>>>>>>>
>>>>>>>>What is this "device" and what is it good for? Smells like some
>>>>>>>>leftover
>>>>>>>>and with the nested scheme looks quite odd.
>>>>>>>>
>>>>>>>
>>>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>>>with netlink:
>>>>>>>
>>>>>>>- dump of device-get command where all devices are returned, each one
>>>>>>>nested
>>>>>>>inside it:
>>>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id':0},
>>>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':1}]}]
>>>>>>
>>>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>>>instance. Is this the real output of you made that up?
>>>>>>
>>>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>>>
>>>>>
>>>>>This was returned by CLI parser on ice with cmd:
>>>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>>>--dump device-get
>>>>>
>>>>>Please note this relates to 'dump' request , it is rather expected that
>>>>there
>>>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>>>
>>>>No, you definitelly don't need to nest them. Dump format and get format
>>>>should be exactly the same. Please remove the nest.
>>>>
>>>>See how that is done in devlink for example: devlink_nl_fill()
>>>>This functions fills up one object in the dump. No nesting.
>>>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>>>not make sense at all.
>>>>
>>>
>>>Yeah it make sense to have same output on `do` and `dump`, but this is also
>>>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>>>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>>>on `do` only one.
>>
>>Please don't. This root nesting is not correct.
>>
>>
>>>
>>>Will try to fix it.
>>>Although could you please explain why it make sense to put extra header
>>>(exactly the same header) multiple times in one netlink response message?
>>
>>This is how it's done for all netlink dumps as far as I know.
>
>So we just following something but we cannot explain why?

I thought it is obvious. With what you suggest, each generic netlink
user would have to have all commands that implement both do and dump
with message format of a root nest. Not only that, for the sake of
consistency, all the rest of the commands would have to implement same
root nesting format. Both ways, to and from kernel.
So no matter what, all the messages would look like:

CMD_X MSG:
  SUBSYS_ATTR_ROOT
    SUBSYS_ATTR_1
    SUBSYS_ATTR_2
    ***
    SUBSYS_ATTR_N

What I say, this extra level of nesting is not needed and in fact it is
not good for anything. Remove it and just have:

CMD_X MSG:
  SUBSYS_ATTR_1
  SUBSYS_ATTR_2
  ***
  SUBSYS_ATTR_N

Clear and simple. For dump, you repeat the header, I don't see why that
is problem. All implementations are doing that, all userspace apps are
used to it. It is simple and consistent even if you consider multiple
skbs used for dump. I'm lacking to understand your need to reinvent
the wheel here.



>
>>The reason might be that the userspace is parsing exactly the same
>>message as if it would be DOIT message.
>>
>
>This argument is achievable on both approaches.
>
>[...]
>
>
>>>>>>>>
>>>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>>>
>>>>>>>No, there is no set for this.
>>>>>>>For manual mode user selects the pin by setting enabled state on the
>>one
>>>>>>>he needs to recover signal from.
>>>>>>>
>>>>>>>source-pin-index is read only, returns active source.
>>>>>>
>>>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>>>the enabled state to serve the user to see which one is selected, no?
>>>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>>>pin index)
>>>>>>
>>>>>
>>>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>>>selection is done on hardware level with priorities.
>>>>
>>>>Okay, this is confusing and I believe wrong.
>>>>You have dual meaning for pin state attribute with states
>>>>STATE_CONNECTED/DISCONNECTED:
>>>>
>>>>1) Manual mode, MUX pins (both share the same model):
>>>>   There is only one pin with STATE_CONNECTED. The others are in
>>>>   STATE_DISCONNECTED
>>>>   User changes a state of a pin to make the selection.
>>>>
>>>>   Example:
>>>>     $ dplltool pin dump
>>>>       pin 1 state connected
>>>>       pin 2 state disconnected
>>>>     $ dplltool pin 2 set state connected
>>>>     $ dplltool pin dump
>>>>       pin 1 state disconnected
>>>>       pin 2 state connected
>>>>
>>>>2) Automatic mode:
>>>>   The user by setting "state" decides it the pin should be considered
>>>>   by the device for auto selection.
>>>>
>>>>   Example:
>>>>     $ dplltool pin dump:
>>>>       pin 1 state connected prio 10
>>>>       pin 2 state connected prio 15
>>>>     $ dplltool dpll x get:
>>>>       dpll x source-pin-index 1
>>>>
>>>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>>>source pin. However, in automatic mode it means something else. It means
>>>>the user allows this pin to be considered for auto selection. The fact
>>>>the pin is selected source is exposed over source-pin-index.
>>>>
>>>>Instead of this, I believe that the semantics of
>>>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>>>well. Unlike the manual mode/mux, where the state is written by user, in
>>>>automatic mode the state should be only written by the driver. User
>>>>attemts to set the state should fail with graceful explanation (DPLL
>>>>netlink/core code should handle that, w/o driver interaction)
>>>>
>>>>Suggested automatic mode example:
>>>>     $ dplltool pin dump:
>>>>       pin 1 state connected prio 10 connectable true
>>>>       pin 2 state disconnected prio 15 connectable true
>>>>     $ dplltool pin 1 set connectable false
>>>>     $ dplltool pin dump:
>>>>       pin 1 state disconnected prio 10 connectable false
>>>>       pin 2 state connected prio 15 connectable true
>>>>     $ dplltool pin 1 set state connected
>>>>       -EOPNOTSUPP
>>>>
>>>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>>>There is a new attribute called "connectable", the user uses this
>>>>attribute to tell the device, if this source pin could be considered for
>>>>auto selection or not.
>>>>
>>>>Could be called perhaps "selectable", does not matter. The point is, the
>>>>meaning of the "state" attribute is consistent for automatic mode,
>>>>manual mode and mux pin.
>>>>
>>>>Makes sense?
>>>>
>>>
>>>Great idea!
>>>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>>>In the end we will have this:
>>>              +--------------------------------+
>>>              | valid DPLL_A_PIN_STATE values  |
>>>	      +---------------+----------------+
>>>+------------+| requested:    | returned:      |
>>>|DPLL_A_MODE:||               |                |
>>>|------------++--------------------------------|
>>>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>>>|            ||- DISCONNECTED | - DISCONNECTED |
>>>|            ||               | - CONNECTED    |
>>
>>"selectable" is something the user sets.
>
>Yes.
>
>>"connected"/"disconnected" is in case of auto mode something that driver
>>sets.
>>
>
>No. Not really.
>"CONNECTED" is only set by driver once a pin is choosen.
>"SELECTABLE" is set by the user if he needs to enable a pin for selection,
>it is also default state of a pin if it was not selected ("CONNECTED")
>"DISCONNECTED" is set by the user if he needs to disable a pin from selection.

Sure.


>
>>Looks a bit odd to mix them together. That is why I suggested
>>to have sepectable as a separate attr. But up to you. Please make sure
>>you sanitize the user/driver set of this attr in dpll code.
>>
>
>What is odd?

To mix them together in a single attr. But I'm fine with that.


>What do you mean by "sanitize the user/driver set of this attr in dpll code"?

What I mean is that in each mode there has to be clearly documented
which entity changes state, how and under which circumstance. This
behaviour needs to be enforced in the dpll code.


>
>
>Thank you,
>Arkadiusz
>
>>
>>>|------------++--------------------------------|
>>>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>>>|            ||- DISCONNECTED | - DISCONNECTED |
>>>+------------++---------------+----------------+
>>>
>>>Thank you,
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>>+
>>>>>>>>>+/* 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),
>>>>>>>>
>>>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>>>here instead of "5"?
>>>>>>>>
>>>>>>>
>>>>>>>Not really, it probably needs a fix for this.
>>>>>>
>>>>>>Yeah.
>>>>>>
>>>>>
>>>>>Well, once we done with review maybe we could also fix those, or ask
>>>>>Jakub if he could help :)
>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>
>>>>>>>>>+	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,
>>>>>>>>
>>>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>>>enum name.
>>>>>>>
>>>>>>>Sure, fixed.
>>>>>>
>>>>>>
>>>>>>Thanks for all your work on this!
>>>>>
>>>>>Thanks for a great review! :)
>>>>
>>>>Glad to help.

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

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

Fri, Mar 17, 2023 at 07:22:46PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, March 17, 2023 5:21 PM
>>
>>Fri, Mar 17, 2023 at 04:14:45PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, March 17, 2023 11:05 AM
>>>>
>>>>Fri, Mar 17, 2023 at 01:52:44AM CET, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, March 16, 2023 2:45 PM
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>>+attribute-sets:
>>>>>>>>>+  -
>>>>>>>>>+    name: dpll
>>>>>>>>>+    enum-name: dplla
>>>>>>>>>+    attributes:
>>>>>>>>>+      -
>>>>>>>>>+        name: device
>>>>>>>>>+        type: nest
>>>>>>>>>+        value: 1
>>>>>>>>>+        multi-attr: true
>>>>>>>>>+        nested-attributes: device
>>>>>>>>
>>>>>>>>What is this "device" and what is it good for? Smells like some
>>>>>>>>leftover
>>>>>>>>and with the nested scheme looks quite odd.
>>>>>>>>
>>>>>>>
>>>>>>>No, it is nested attribute type, used when multiple devices are returned
>>>>>>>with netlink:
>>>>>>>
>>>>>>>- dump of device-get command where all devices are returned, each one
>>>>>>>nested
>>>>>>>inside it:
>>>>>>>[{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0', 'id':0},
>>>>>>>             {'bus-name': 'pci', 'dev-name': '0000:21:00.0_1', 'id':1}]}]
>>>>>>
>>>>>>Okay, why is it nested here? The is one netlink msg per dpll device
>>>>>>instance. Is this the real output of you made that up?
>>>>>>
>>>>>>Device nest should not be there for DEVICE_GET, does not make sense.
>>>>>>
>>>>>
>>>>>This was returned by CLI parser on ice with cmd:
>>>>>$ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml /
>>>>>--dump device-get
>>>>>
>>>>>Please note this relates to 'dump' request , it is rather expected that
>>>>there
>>>>>are multiple dplls returned, thus we need a nest attribute for each one.
>>>>
>>>>No, you definitelly don't need to nest them. Dump format and get format
>>>>should be exactly the same. Please remove the nest.
>>>>
>>>>See how that is done in devlink for example: devlink_nl_fill()
>>>>This functions fills up one object in the dump. No nesting.
>>>>I'm not aware of such nesting approach anywhere in kernel dumps, does
>>>>not make sense at all.
>>>>
>>>
>>>Yeah it make sense to have same output on `do` and `dump`, but this is also
>>>achievable with nest DPLL_A_DEVICE, still don't need put extra header for it.
>>>The difference would be that on `dump` multiple DPLL_A_DEVICE are provided,
>>>on `do` only one.
>>
>>Please don't. This root nesting is not correct.
>>
>>
>>>
>>>Will try to fix it.
>>>Although could you please explain why it make sense to put extra header
>>>(exactly the same header) multiple times in one netlink response message?
>>
>>This is how it's done for all netlink dumps as far as I know.
>
>So we just following something but we cannot explain why?

I thought it is obvious. With what you suggest, each generic netlink
user would have to have all commands that implement both do and dump
with message format of a root nest. Not only that, for the sake of
consistency, all the rest of the commands would have to implement same
root nesting format. Both ways, to and from kernel.
So no matter what, all the messages would look like:

CMD_X MSG:
  SUBSYS_ATTR_ROOT
    SUBSYS_ATTR_1
    SUBSYS_ATTR_2
    ***
    SUBSYS_ATTR_N

What I say, this extra level of nesting is not needed and in fact it is
not good for anything. Remove it and just have:

CMD_X MSG:
  SUBSYS_ATTR_1
  SUBSYS_ATTR_2
  ***
  SUBSYS_ATTR_N

Clear and simple. For dump, you repeat the header, I don't see why that
is problem. All implementations are doing that, all userspace apps are
used to it. It is simple and consistent even if you consider multiple
skbs used for dump. I'm lacking to understand your need to reinvent
the wheel here.



>
>>The reason might be that the userspace is parsing exactly the same
>>message as if it would be DOIT message.
>>
>
>This argument is achievable on both approaches.
>
>[...]
>
>
>>>>>>>>
>>>>>>>>Hmm, shouldn't source-pin-index be here as well?
>>>>>>>
>>>>>>>No, there is no set for this.
>>>>>>>For manual mode user selects the pin by setting enabled state on the
>>one
>>>>>>>he needs to recover signal from.
>>>>>>>
>>>>>>>source-pin-index is read only, returns active source.
>>>>>>
>>>>>>Okay, got it. Then why do we have this assymetric approach? Just have
>>>>>>the enabled state to serve the user to see which one is selected, no?
>>>>>>This would help to avoid confusion (like mine) and allow not to create
>>>>>>inconsistencies (like no pin enabled yet driver to return some source
>>>>>>pin index)
>>>>>>
>>>>>
>>>>>This is due to automatic mode were multiple pins are enabled, but actual
>>>>>selection is done on hardware level with priorities.
>>>>
>>>>Okay, this is confusing and I believe wrong.
>>>>You have dual meaning for pin state attribute with states
>>>>STATE_CONNECTED/DISCONNECTED:
>>>>
>>>>1) Manual mode, MUX pins (both share the same model):
>>>>   There is only one pin with STATE_CONNECTED. The others are in
>>>>   STATE_DISCONNECTED
>>>>   User changes a state of a pin to make the selection.
>>>>
>>>>   Example:
>>>>     $ dplltool pin dump
>>>>       pin 1 state connected
>>>>       pin 2 state disconnected
>>>>     $ dplltool pin 2 set state connected
>>>>     $ dplltool pin dump
>>>>       pin 1 state disconnected
>>>>       pin 2 state connected
>>>>
>>>>2) Automatic mode:
>>>>   The user by setting "state" decides it the pin should be considered
>>>>   by the device for auto selection.
>>>>
>>>>   Example:
>>>>     $ dplltool pin dump:
>>>>       pin 1 state connected prio 10
>>>>       pin 2 state connected prio 15
>>>>     $ dplltool dpll x get:
>>>>       dpll x source-pin-index 1
>>>>
>>>>So in manual mode, STATE_CONNECTED means the dpll is connected to this
>>>>source pin. However, in automatic mode it means something else. It means
>>>>the user allows this pin to be considered for auto selection. The fact
>>>>the pin is selected source is exposed over source-pin-index.
>>>>
>>>>Instead of this, I believe that the semantics of
>>>>STATE_CONNECTED/DISCONNECTED should be the same for automatic mode as
>>>>well. Unlike the manual mode/mux, where the state is written by user, in
>>>>automatic mode the state should be only written by the driver. User
>>>>attemts to set the state should fail with graceful explanation (DPLL
>>>>netlink/core code should handle that, w/o driver interaction)
>>>>
>>>>Suggested automatic mode example:
>>>>     $ dplltool pin dump:
>>>>       pin 1 state connected prio 10 connectable true
>>>>       pin 2 state disconnected prio 15 connectable true
>>>>     $ dplltool pin 1 set connectable false
>>>>     $ dplltool pin dump:
>>>>       pin 1 state disconnected prio 10 connectable false
>>>>       pin 2 state connected prio 15 connectable true
>>>>     $ dplltool pin 1 set state connected
>>>>       -EOPNOTSUPP
>>>>
>>>>Note there is no "source-pin-index" at all. Replaced by pin state here.
>>>>There is a new attribute called "connectable", the user uses this
>>>>attribute to tell the device, if this source pin could be considered for
>>>>auto selection or not.
>>>>
>>>>Could be called perhaps "selectable", does not matter. The point is, the
>>>>meaning of the "state" attribute is consistent for automatic mode,
>>>>manual mode and mux pin.
>>>>
>>>>Makes sense?
>>>>
>>>
>>>Great idea!
>>>I will add third enum for pin-state: DPLL_PIN_STATE_SELECTABLE.
>>>In the end we will have this:
>>>              +--------------------------------+
>>>              | valid DPLL_A_PIN_STATE values  |
>>>	      +---------------+----------------+
>>>+------------+| requested:    | returned:      |
>>>|DPLL_A_MODE:||               |                |
>>>|------------++--------------------------------|
>>>|AUTOMATIC   ||- SELECTABLE   | - SELECTABLE   |
>>>|            ||- DISCONNECTED | - DISCONNECTED |
>>>|            ||               | - CONNECTED    |
>>
>>"selectable" is something the user sets.
>
>Yes.
>
>>"connected"/"disconnected" is in case of auto mode something that driver
>>sets.
>>
>
>No. Not really.
>"CONNECTED" is only set by driver once a pin is choosen.
>"SELECTABLE" is set by the user if he needs to enable a pin for selection,
>it is also default state of a pin if it was not selected ("CONNECTED")
>"DISCONNECTED" is set by the user if he needs to disable a pin from selection.

Sure.


>
>>Looks a bit odd to mix them together. That is why I suggested
>>to have sepectable as a separate attr. But up to you. Please make sure
>>you sanitize the user/driver set of this attr in dpll code.
>>
>
>What is odd?

To mix them together in a single attr. But I'm fine with that.


>What do you mean by "sanitize the user/driver set of this attr in dpll code"?

What I mean is that in each mode there has to be clearly documented
which entity changes state, how and under which circumstance. This
behaviour needs to be enforced in the dpll code.


>
>
>Thank you,
>Arkadiusz
>
>>
>>>|------------++--------------------------------|
>>>|MANUAL      ||- CONNECTED    | - CONNECTED    |
>>>|            ||- DISCONNECTED | - DISCONNECTED |
>>>+------------++---------------+----------------+
>>>
>>>Thank you,
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>>+
>>>>>>>>>+/* 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),
>>>>>>>>
>>>>>>>>Hmm, any idea why the generator does not put define name
>>>>>>>>here instead of "5"?
>>>>>>>>
>>>>>>>
>>>>>>>Not really, it probably needs a fix for this.
>>>>>>
>>>>>>Yeah.
>>>>>>
>>>>>
>>>>>Well, once we done with review maybe we could also fix those, or ask
>>>>>Jakub if he could help :)
>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>>>>
>>>>>>>>>+	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,
>>>>>>>>
>>>>>>>>Just DPLL_A_PIN_CAPS is enough, that would be also consistent with the
>>>>>>>>enum name.
>>>>>>>
>>>>>>>Sure, fixed.
>>>>>>
>>>>>>
>>>>>>Thanks for all your work on this!
>>>>>
>>>>>Thanks for a great review! :)
>>>>
>>>>Glad to help.

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+static int
>+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref = NULL;

Pointless init.

[...]


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

[...]


>+struct dpll_pin_ops {
>+	int (*frequency_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const u32 frequency,
>+			     struct netlink_ext_ack *extack);
>+	int (*frequency_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     u32 *frequency, struct netlink_ext_ack *extack);
>+	int (*direction_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const enum dpll_pin_direction direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*direction_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     enum dpll_pin_direction *direction,
>+			     struct netlink_ext_ack *extack);


For frequency and direction, why exactly do you need to pass dpll
pointer? For set I can understand you need it to set the same
frequency/direction for all dplls the pin is connected to, but why for
get()?

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+static int
>+dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref = NULL;

Pointless init.

[...]


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

[...]


>+struct dpll_pin_ops {
>+	int (*frequency_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const u32 frequency,
>+			     struct netlink_ext_ack *extack);
>+	int (*frequency_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     u32 *frequency, struct netlink_ext_ack *extack);
>+	int (*direction_set)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     const enum dpll_pin_direction direction,
>+			     struct netlink_ext_ack *extack);
>+	int (*direction_get)(const struct dpll_pin *pin,
>+			     const struct dpll_device *dpll,
>+			     enum dpll_pin_direction *direction,
>+			     struct netlink_ext_ack *extack);


For frequency and direction, why exactly do you need to pass dpll
pointer? For set I can understand you need it to set the same
frequency/direction for all dplls the pin is connected to, but why for
get()?

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-17 16:23     ` Jiri Pirko
@ 2023-03-21  4:00       ` Jakub Kicinski
  -1 siblings, 0 replies; 202+ messages in thread
From: Jakub Kicinski @ 2023-03-21  4:00 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Michal Michalik

On Fri, 17 Mar 2023 17:23:33 +0100 Jiri Pirko wrote:
> >+/* Ops table for dpll */
> >+static const struct genl_split_ops dpll_nl_ops[6] = {  
> 
> It's odd to see 6 here. Should by just []
> But it's an issue of Jakub's generator.

In some modes of generation the array is visible outside the source file
i.e. there's a forward declaration in the header. So I had to get the
counting right, anyway. But sure, I'll send a "fix"..

_______________________________________________
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] 202+ messages in thread

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

On Fri, 17 Mar 2023 17:23:33 +0100 Jiri Pirko wrote:
> >+/* Ops table for dpll */
> >+static const struct genl_split_ops dpll_nl_ops[6] = {  
> 
> It's odd to see 6 here. Should by just []
> But it's an issue of Jakub's generator.

In some modes of generation the array is visible outside the source file
i.e. there's a forward declaration in the header. So I had to get the
counting right, anyway. But sure, I'll send a "fix"..

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

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-16 13:15       ` Kubalewski, Arkadiusz
@ 2023-03-21  4:05         ` Jakub Kicinski
  -1 siblings, 0 replies; 202+ messages in thread
From: Jakub Kicinski @ 2023-03-21  4:05 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jiri Pirko, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

On Thu, 16 Mar 2023 13:15:59 +0000 Kubalewski, Arkadiusz wrote:
> >Value 23? What's this?
> >You have it specified for some attrs all over the place.
> >What is the reason for it?
> >  
> 
> Actually this particular one is not needed (also value: 12 on pin above),
> I will remove those.
> But the others you are refering to (the ones in nested attribute list),
> are required because of cli.py parser issue, maybe Kuba knows a better way to
> prevent the issue?
> Basically, without those values, cli.py brakes on parsing responses, after
> every "jump" to nested attribute list it is assigning first attribute there
> with value=0, thus there is a need to assign a proper value, same as it is on
> 'main' attribute list.

Are you saying the parser gets confused after returning from nested
parsing? Can you still repro this problem? I don't see any global
state in _decode()..

_______________________________________________
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] 202+ messages in thread

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

On Thu, 16 Mar 2023 13:15:59 +0000 Kubalewski, Arkadiusz wrote:
> >Value 23? What's this?
> >You have it specified for some attrs all over the place.
> >What is the reason for it?
> >  
> 
> Actually this particular one is not needed (also value: 12 on pin above),
> I will remove those.
> But the others you are refering to (the ones in nested attribute list),
> are required because of cli.py parser issue, maybe Kuba knows a better way to
> prevent the issue?
> Basically, without those values, cli.py brakes on parsing responses, after
> every "jump" to nested attribute list it is assigning first attribute there
> with value=0, thus there is a need to assign a proper value, same as it is on
> 'main' attribute list.

Are you saying the parser gets confused after returning from nested
parsing? Can you still repro this problem? I don't see any global
state in _decode()..

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

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

On Mon, 20 Mar 2023 21:05:49 -0700 Jakub Kicinski wrote:
> > Actually this particular one is not needed (also value: 12 on pin above),
> > I will remove those.
> > But the others you are refering to (the ones in nested attribute list),
> > are required because of cli.py parser issue, maybe Kuba knows a better way to
> > prevent the issue?
> > Basically, without those values, cli.py brakes on parsing responses, after
> > every "jump" to nested attribute list it is assigning first attribute there
> > with value=0, thus there is a need to assign a proper value, same as it is on
> > 'main' attribute list.  
> 
> Are you saying the parser gets confused after returning from nested
> parsing? Can you still repro this problem? I don't see any global
> state in _decode()..

Oh, I grokked the attr structure. This should be fixed now.
Commit 7cf93538e087 ("tools: ynl: fully inherit attrs in subsets")

The entire attr structure is a bit off (/ inspired by devlink?)
Why create one huge attribute space with all attributes in it?
Try to find something in the devlink attrs, it's pages long with 
little to no reuse :| Can't pins and devices have their own spaces?

If one has to carry a link to the other you should create a subset
so there is no circular dependency. That's the only potentially tricky
thing I can think of...

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

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

On Mon, 20 Mar 2023 21:05:49 -0700 Jakub Kicinski wrote:
> > Actually this particular one is not needed (also value: 12 on pin above),
> > I will remove those.
> > But the others you are refering to (the ones in nested attribute list),
> > are required because of cli.py parser issue, maybe Kuba knows a better way to
> > prevent the issue?
> > Basically, without those values, cli.py brakes on parsing responses, after
> > every "jump" to nested attribute list it is assigning first attribute there
> > with value=0, thus there is a need to assign a proper value, same as it is on
> > 'main' attribute list.  
> 
> Are you saying the parser gets confused after returning from nested
> parsing? Can you still repro this problem? I don't see any global
> state in _decode()..

Oh, I grokked the attr structure. This should be fixed now.
Commit 7cf93538e087 ("tools: ynl: fully inherit attrs in subsets")

The entire attr structure is a bit off (/ inspired by devlink?)
Why create one huge attribute space with all attributes in it?
Try to find something in the devlink attrs, it's pages long with 
little to no reuse :| Can't pins and devices have their own spaces?

If one has to carry a link to the other you should create a subset
so there is no circular dependency. That's the only potentially tricky
thing I can think of...

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML
  2023-03-21  4:13           ` Jakub Kicinski
@ 2023-03-21  4:20             ` Jakub Kicinski
  -1 siblings, 0 replies; 202+ messages in thread
From: Jakub Kicinski @ 2023-03-21  4:20 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jiri Pirko, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	Vadim Fedorenko, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Michalik, Michal

On Mon, 20 Mar 2023 21:13:54 -0700 Jakub Kicinski wrote:
> If one has to carry a link to the other you should create a subset
> so there is no circular dependency. That's the only potentially tricky
> thing I can think of...

Another option is to take a page from ethtool's book and factor out
identifiers to a nested attr from the start.

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

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

On Mon, 20 Mar 2023 21:13:54 -0700 Jakub Kicinski wrote:
> If one has to carry a link to the other you should create a subset
> so there is no circular dependency. That's the only potentially tricky
> thing I can think of...

Another option is to take a page from ethtool's book and factor out
identifiers to a nested attr from the start.

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]

>+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->dev_driver_id))
>+		return -EMSGSIZE;
>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_SOURCE_PIN_IDX:
>+		ret = dpll_msg_add_source_pin_idx(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_LOCK_STATUS:
>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);

On top of what I wrote about the notifications, I found another two
issues:
1) You don't take any lock calling this from drivers. You need to hold
   the xarray locks you have now.

   I have to repear, I think that we definitelly need to convert the
   overall locking scheme to have this per-instance, in a similar way
   we did that for devlink. I noted this in another email, but wanted
   to say that again.

2) You have possible race condition:
   1) -> driver gets a state change event
   2) -> driver calls into this function
   3) -> this code does call the driver op to get the state, driver
         queries the state again

   Between 1) and 3) state can easily change, multiple times. That might
   lead to oddities observed by the user (like getting a notification
   of change with the original values)

   I see only 1 solutions to this:
   Pass the value of changed item from the driver here and just pass
   it on over netlink without doing calling into driver again.


>+		break;
>+	case DPLL_A_TEMP:
>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_PIN_FREQUENCY:
>+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+			if (!ref)
>+				return -EFAULT;
>+			if (!ref->ops || !ref->ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>+							 NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->dev_driver_id))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+			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)

Please change the function names to "register/unregister" to be
consistent with the rest of the code.


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

The driver should be aware of netlink attributes. Should be
abstracted out.

just have per-item notification like:
dpll_pin_state_notify()
dpll_pin_prio_notify()
...

Then you can easily pass changed value that would allow solution to
the issue 2) I described above.



>+{
>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>+}
>+

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]

>+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->dev_driver_id))
>+		return -EMSGSIZE;
>+
>+	switch (attr) {
>+	case DPLL_A_MODE:
>+		ret = dpll_msg_add_mode(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_SOURCE_PIN_IDX:
>+		ret = dpll_msg_add_source_pin_idx(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_LOCK_STATUS:
>+		ret = dpll_msg_add_lock_status(msg, dpll, NULL);

On top of what I wrote about the notifications, I found another two
issues:
1) You don't take any lock calling this from drivers. You need to hold
   the xarray locks you have now.

   I have to repear, I think that we definitelly need to convert the
   overall locking scheme to have this per-instance, in a similar way
   we did that for devlink. I noted this in another email, but wanted
   to say that again.

2) You have possible race condition:
   1) -> driver gets a state change event
   2) -> driver calls into this function
   3) -> this code does call the driver op to get the state, driver
         queries the state again

   Between 1) and 3) state can easily change, multiple times. That might
   lead to oddities observed by the user (like getting a notification
   of change with the original values)

   I see only 1 solutions to this:
   Pass the value of changed item from the driver here and just pass
   it on over netlink without doing calling into driver again.


>+		break;
>+	case DPLL_A_TEMP:
>+		ret = dpll_msg_add_temp(msg, dpll, NULL);
>+		break;
>+	case DPLL_A_PIN_FREQUENCY:
>+		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>+		break;
>+	case DPLL_A_PIN_PRIO:
>+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+		if (!ref)
>+			return -EFAULT;
>+		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>+		break;
>+	case DPLL_A_PIN_STATE:
>+		if (parent) {
>+			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+			if (!ref)
>+				return -EFAULT;
>+			if (!ref->ops || !ref->ops->state_on_pin_get)
>+				return -EOPNOTSUPP;
>+			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>+							 NULL);
>+			if (ret)
>+				return ret;
>+			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>+					parent->dev_driver_id))
>+				return -EMSGSIZE;
>+		} else {
>+			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+			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)

Please change the function names to "register/unregister" to be
consistent with the rest of the code.


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

The driver should be aware of netlink attributes. Should be
abstracted out.

just have per-item notification like:
dpll_pin_state_notify()
dpll_pin_prio_notify()
...

Then you can easily pass changed value that would allow solution to
the issue 2) I described above.



>+{
>+	return dpll_send_event_change(dpll, pin, NULL, attr);
>+}
>+

[...]

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+/**
>+ * 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 references are dropped, delete the reference and free its memory.
>+ *

Hmm, came to think about this, why do you do func docs even for static
function? It is customary to do that for exported function. For static
ones, not really needed.


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

Have this to return void, you don't check the return value anywhere.


>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_pins, i);
>+				kfree(ref);
>+			}
>+			return 0;
>+		}
>+	}
>+
>+	return -EINVAL;
>+}

[...]


>+/**
>+ * 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)

Every caller of this function does fill the first arg by:
&pin->dpll_refs
Could you please change it to "struct dpll_pin *pin" and get the xarray
pointer in this function?

The same applies to other functions passing xarray pointer, like:
dpll_xa_ref_dpll_add
dpll_xa_ref_dpll_del
dpll_xa_ref_pin_find

The point is, always better and easier to read to pass
"struct dpll_device *" and "struct dpll_pin *" as function args.
Passing "struct xarray *" makes the reader uncertain about what
is going on.



>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_refs, i, ref) {
>+		if (ref->dpll == dpll)
>+			return ref;
>+	}
>+
>+	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

You return more than that. Do you really need to list the error
possiblities in the func docs?


>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)

[...]


>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	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;
>+	}

Somewhere here, please add a check:
dpll->module == pin->module dpll->clock_id && pin->clock_id
For sanity sake.


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

Pointless else.


>+
>+	return ret;
>+
>+ref_pin_del:
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>+rclk_free:
>+	kfree(pin->rclk_dev_name);
>+	return ret;
>+}

[...]


>+int
>+dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+			 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(!pin || !parent))
>+		return -EINVAL;
>+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>+		return -EPERM;
>+	mutex_lock(&dpll_pin_xa_lock);
>+	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_device_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,

Why exactly do you need to register the pin over to the dpll of a
parent? Isn't it enough to have the pin registered on a parent?
I mean, there is no direct connection between pin and dpll, the parent
is in the middle. So prio setup, and other things does not make sense to
configure on this child pin, isn't it?

Btw, what is stopping the driver from:
dpll register
pin1 register on dpll
pin2 register on pin1
pin1 unregister
?
The you would have pin2 registered to dpll incorrectly.


>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_device_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_device_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin);
>+			mutex_unlock(&dpll_device_xa_lock);
>+		}
>+	}
>+	refcount_dec(&pin->refcount);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+unlock:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	return ret;
>+}

[...]


>+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)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *parent;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))

Hmm, why is this capabilities are any good for internal purposes? I
understand the need to expose it to the user, but internally in kernel,
if the driver implements some _set() op, it is good enough indication of
a support of a certain setter. You can check if the relevant _set()
is not null and expose the appropriate capability to the user.



>+		return -EOPNOTSUPP;
>+	parent = dpll_pin_get_by_idx(dpll, parent_idx);

I don't follow. Why do you need dpll pointer to get the parent pin?
The same handle as pin should be used, you have clock_id and driver name
(in next patchsets implementation) that should be enough.
Pin is a separate entity, attached 0:N dplls.

Please remove dpll pointer from here. Also, please remove
dpll->pins_ref, as you are using this array only for this lookup (here
and in dpll_pin_pre_doit())


>+	if (!parent)
>+		return -EINVAL;
>+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+	if (!ref)
>+		return -EINVAL;
>+	if (!ref->ops || !ref->ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin, parent, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->state_on_dpll_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
>+		return -EINVAL;
>+	dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_STATE);
>+
>+	return 0;
>+}

[...]


>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	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);
>+
>+			if (!dpll->ops || !dpll->ops->mode_set)

Remove the pointless check of ops. This cannot happen (checked in
dpll_device_register())


>+				return -EOPNOTSUPP;
>+			ret = dpll->ops->mode_set(dpll, mode, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+

[...]

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+/**
>+ * 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 references are dropped, delete the reference and free its memory.
>+ *

Hmm, came to think about this, why do you do func docs even for static
function? It is customary to do that for exported function. For static
ones, not really needed.


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

Have this to return void, you don't check the return value anywhere.


>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_pins, i, ref) {
>+		if (ref->pin == pin) {
>+			if (refcount_dec_and_test(&ref->refcount)) {
>+				xa_erase(xa_pins, i);
>+				kfree(ref);
>+			}
>+			return 0;
>+		}
>+	}
>+
>+	return -EINVAL;
>+}

[...]


>+/**
>+ * 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)

Every caller of this function does fill the first arg by:
&pin->dpll_refs
Could you please change it to "struct dpll_pin *pin" and get the xarray
pointer in this function?

The same applies to other functions passing xarray pointer, like:
dpll_xa_ref_dpll_add
dpll_xa_ref_dpll_del
dpll_xa_ref_pin_find

The point is, always better and easier to read to pass
"struct dpll_device *" and "struct dpll_pin *" as function args.
Passing "struct xarray *" makes the reader uncertain about what
is going on.



>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each(xa_refs, i, ref) {
>+		if (ref->dpll == dpll)
>+			return ref;
>+	}
>+
>+	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

You return more than that. Do you really need to list the error
possiblities in the func docs?


>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+			 struct dpll_device_ops *ops, void *priv,
>+			 struct device *owner)

[...]


>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    struct dpll_pin_ops *ops, void *priv,
>+		    const char *rclk_device_name)
>+{
>+	int ret;
>+
>+	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;
>+	}

Somewhere here, please add a check:
dpll->module == pin->module dpll->clock_id && pin->clock_id
For sanity sake.


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

Pointless else.


>+
>+	return ret;
>+
>+ref_pin_del:
>+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
>+rclk_free:
>+	kfree(pin->rclk_dev_name);
>+	return ret;
>+}

[...]


>+int
>+dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+			 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(!pin || !parent))
>+		return -EINVAL;
>+	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
>+		return -EPERM;
>+	mutex_lock(&dpll_pin_xa_lock);
>+	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_device_xa_lock);
>+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv,

Why exactly do you need to register the pin over to the dpll of a
parent? Isn't it enough to have the pin registered on a parent?
I mean, there is no direct connection between pin and dpll, the parent
is in the middle. So prio setup, and other things does not make sense to
configure on this child pin, isn't it?

Btw, what is stopping the driver from:
dpll register
pin1 register on dpll
pin2 register on pin1
pin1 unregister
?
The you would have pin2 registered to dpll incorrectly.


>+					  rclk_device ?
>+					  dev_name(rclk_device) : NULL);
>+		mutex_unlock(&dpll_device_xa_lock);
>+		if (ret) {
>+			stop = i;
>+			goto dpll_unregister;
>+		}
>+		dpll_pin_parent_notify(ref->dpll, pin, parent, DPLL_A_PIN_IDX);
>+	}
>+	mutex_unlock(&dpll_pin_xa_lock);
>+
>+	return ret;
>+
>+dpll_unregister:
>+	xa_for_each(&parent->dpll_refs, i, ref) {
>+		if (i < stop) {
>+			mutex_lock(&dpll_device_xa_lock);
>+			__dpll_pin_unregister(ref->dpll, pin);
>+			mutex_unlock(&dpll_device_xa_lock);
>+		}
>+	}
>+	refcount_dec(&pin->refcount);
>+	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
>+unlock:
>+	mutex_unlock(&dpll_pin_xa_lock);
>+	return ret;
>+}

[...]


>+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)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *parent;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))

Hmm, why is this capabilities are any good for internal purposes? I
understand the need to expose it to the user, but internally in kernel,
if the driver implements some _set() op, it is good enough indication of
a support of a certain setter. You can check if the relevant _set()
is not null and expose the appropriate capability to the user.



>+		return -EOPNOTSUPP;
>+	parent = dpll_pin_get_by_idx(dpll, parent_idx);

I don't follow. Why do you need dpll pointer to get the parent pin?
The same handle as pin should be used, you have clock_id and driver name
(in next patchsets implementation) that should be enough.
Pin is a separate entity, attached 0:N dplls.

Please remove dpll pointer from here. Also, please remove
dpll->pins_ref, as you are using this array only for this lookup (here
and in dpll_pin_pre_doit())


>+	if (!parent)
>+		return -EINVAL;
>+	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>+	if (!ref)
>+		return -EINVAL;
>+	if (!ref->ops || !ref->ops->state_on_pin_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
>+		return -EFAULT;
>+	dpll_pin_parent_notify(dpll, pin, parent, 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)
>+{
>+	struct dpll_pin_ref *ref;
>+
>+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop.capabilities))
>+		return -EOPNOTSUPP;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)
>+		return -EFAULT;
>+	if (!ref->ops || !ref->ops->state_on_dpll_set)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
>+		return -EINVAL;
>+	dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_STATE);
>+
>+	return 0;
>+}

[...]


>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	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);
>+
>+			if (!dpll->ops || !dpll->ops->mode_set)

Remove the pointless check of ops. This cannot happen (checked in
dpll_device_register())


>+				return -EOPNOTSUPP;
>+			ret = dpll->ops->mode_set(dpll, mode, info->extack);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-23 11:21   ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-23 11:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and
>make it flexible and easy to cover special configurations.
>
>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


Vadim, Arkadiusz.

I have couple of patches on top of this one and mlx5 implementation.
I would like to send it to you until Sunday. Could you include those to
the next rfc?

I will not review this version more now. Lets do the changes, I will
continue with the review on the next version of rfc.

Thanks!

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-23 11:21   ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-23 11:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Vadim Fedorenko, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and
>make it flexible and easy to cover special configurations.
>
>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


Vadim, Arkadiusz.

I have couple of patches on top of this one and mlx5 implementation.
I would like to send it to you until Sunday. Could you include those to
the next rfc?

I will not review this version more now. Lets do the changes, I will
continue with the review on the next version of rfc.

Thanks!

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
  2023-03-23 11:21   ` Jiri Pirko
@ 2023-03-23 18:00     ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-23 18:00 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 23/03/2023 11:21, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> Implement common API for clock/DPLL configuration and status reporting.
>> The API utilises netlink interface as transport for commands and event
>> notifications. This API aim to extend current pin configuration and
>> make it flexible and easy to cover special configurations.
>>
>> 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
> 
> 
> Vadim, Arkadiusz.
> 
> I have couple of patches on top of this one and mlx5 implementation.
> I would like to send it to you until Sunday. Could you include those to
> the next rfc?
> 
> I will not review this version more now. Lets do the changes, I will
> continue with the review on the next version of rfc.
> 
> Thanks!

Hi Jiri!

I'm going to spend Friday and weekend polishing the series to address as 
much comments as I can and publish v7 next week. You can send your 
patches prepared on top of github version, that would be the best way to 
have them ready for the next round.

Thanks,
Vadim

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

* Re: [PATCH RFC v6 0/6] Create common DPLL/clock configuration API
@ 2023-03-23 18:00     ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-23 18:00 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk

On 23/03/2023 11:21, Jiri Pirko wrote:
> Sun, Mar 12, 2023 at 03:28:01AM CET, vadfed@meta.com wrote:
>> Implement common API for clock/DPLL configuration and status reporting.
>> The API utilises netlink interface as transport for commands and event
>> notifications. This API aim to extend current pin configuration and
>> make it flexible and easy to cover special configurations.
>>
>> 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
> 
> 
> Vadim, Arkadiusz.
> 
> I have couple of patches on top of this one and mlx5 implementation.
> I would like to send it to you until Sunday. Could you include those to
> the next rfc?
> 
> I will not review this version more now. Lets do the changes, I will
> continue with the review on the next version of rfc.
> 
> Thanks!

Hi Jiri!

I'm going to spend Friday and weekend polishing the series to address as 
much comments as I can and publish v7 next week. You can send your 
patches prepared on top of github version, that would be the best way to 
have them ready for the next round.

Thanks,
Vadim

_______________________________________________
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] 202+ messages in thread

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+static int
>+dpll_msg_add_pin_direction(struct sk_buff *msg, const struct dpll_pin *pin,
>+			   struct netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->direction_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)

Checking for "ref" is nonsense here, as xa_for_each fills it up
for every iteration.

ref->dpll is always filled. Also pointless check.

Does it make sense to register with ops==NULL? I think we should
forbid it and make this just xa_find(0) to get the first item in the
xarray.

I'm doing this in my patch, as it is dependency on some other patch I do
in this area.


>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))
>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)

Why -EOPNOTSUPP here is not ok, as for the others below?


>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)

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


>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		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, bool dump_dpll)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {

How dpll refs could be empty? I don't think it is possible.

Overall, whole the code has very odd habit of checking for conditions
that are obviously impossible to happen. Only confuses reader as he
naturally expects that the check is there for a reason.



>+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>+		if (ret)
>+			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;
>+}
>+

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

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

Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>+static int
>+dpll_msg_add_pin_direction(struct sk_buff *msg, const struct dpll_pin *pin,
>+			   struct netlink_ext_ack *extack)
>+{
>+	enum dpll_pin_direction direction;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)
>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->direction_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->direction_get(pin, ref->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 netlink_ext_ack *extack, bool dump_any_freq)
>+{
>+	enum dpll_pin_freq_supp fs;
>+	struct dpll_pin_ref *ref;
>+	unsigned long i;
>+	u32 freq;
>+
>+	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
>+		if (ref && ref->ops && ref->dpll)

Checking for "ref" is nonsense here, as xa_for_each fills it up
for every iteration.

ref->dpll is always filled. Also pointless check.

Does it make sense to register with ops==NULL? I think we should
forbid it and make this just xa_find(0) to get the first item in the
xarray.

I'm doing this in my patch, as it is dependency on some other patch I do
in this area.


>+			break;
>+	}
>+	if (!ref || !ref->ops || !ref->dpll)
>+		return -ENODEV;
>+	if (!ref->ops->frequency_get)
>+		return -EOPNOTSUPP;
>+	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
>+		return -EFAULT;
>+	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
>+		return -EMSGSIZE;
>+	if (!dump_any_freq)
>+		return 0;
>+	for (fs = DPLL_PIN_FREQ_SUPP_UNSPEC + 1;
>+	     fs <= DPLL_PIN_FREQ_SUPP_MAX; fs++) {
>+		if (test_bit(fs, &pin->prop.freq_supported)) {
>+			if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED,
>+			    dpll_pin_freq_value[fs]))
>+				return -EMSGSIZE;
>+		}
>+	}
>+	if (pin->prop.any_freq_min && pin->prop.any_freq_max) {
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MIN,
>+				pin->prop.any_freq_min))
>+			return -EMSGSIZE;
>+		if (nla_put_u32(msg, DPLL_A_PIN_ANY_FREQUENCY_MAX,
>+				pin->prop.any_freq_max))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+

[...]


>+static int
>+dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
>+			 struct dpll_device *dpll,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct dpll_pin_ref *ref;
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		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, extack);
>+	if (ret)

Why -EOPNOTSUPP here is not ok, as for the others below?


>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>+	if (!ref)

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


>+		return -EFAULT;
>+	ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pin_parents(msg, pin, extack);
>+	if (ret)
>+		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, bool dump_dpll)
>+{
>+	int ret;
>+
>+	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>+		return -EMSGSIZE;
>+	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
>+		return -EMSGSIZE;
>+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
>+		return -EMSGSIZE;
>+	ret = dpll_msg_add_pin_direction(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
>+	if (ret && ret != -EOPNOTSUPP)
>+		return ret;
>+	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
>+	if (ret)
>+		return ret;
>+	if (!xa_empty(&pin->dpll_refs) && dump_dpll) {

How dpll refs could be empty? I don't think it is possible.

Overall, whole the code has very odd habit of checking for conditions
that are obviously impossible to happen. Only confuses reader as he
naturally expects that the check is there for a reason.



>+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>+		if (ret)
>+			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;
>+}
>+

_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
  2023-03-12  2:28 ` Vadim Fedorenko
@ 2023-03-26 17:00   ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Hi.

This is extending your patchset. Basically, I do this on top of the
changes I pointed out during review. For example patch #6 is exposing
pin handle which is going to change, etc (there, I put a note).

First 5 patches are just needed dependencies and you can squash them
into your patch/patches. Last two patches should go in separatelly.

Please note that the patch #6 is replacing the need to pass the rclk
device during pin registration by putting a link between netdev and dpll
pin.

Please merge this into your dpll patchset and include it in the next
RFC. Thanks!

Jiri Pirko (7):
  dpll: make ops function args const
  dpll: allow to call device register multiple times
  dpll: introduce a helper to get first dpll ref and use it
  dpll: allow to call pin register multiple times
  dpll: export dpll_pin_notify()
  netdev: expose DPLL pin handle for netdevice
  mlx5: Implement SyncE support using DPLL infrastructure

 drivers/dpll/dpll_core.c                      | 334 ++++++++++----
 drivers/dpll/dpll_core.h                      |  25 +-
 drivers/dpll/dpll_netlink.c                   | 161 ++++---
 drivers/dpll/dpll_netlink.h                   |   3 -
 drivers/net/ethernet/intel/ice/ice_dpll.c     |  22 +-
 .../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    | 429 ++++++++++++++++++
 drivers/ptp/ptp_ocp.c                         |   2 +-
 include/linux/dpll.h                          |  46 +-
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 include/linux/netdevice.h                     |   7 +
 include/uapi/linux/if_link.h                  |   2 +
 net/core/dev.c                                |  20 +
 net/core/rtnetlink.c                          |  38 ++
 17 files changed, 1008 insertions(+), 170 deletions(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c

-- 
2.39.0


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

* [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
@ 2023-03-26 17:00   ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Hi.

This is extending your patchset. Basically, I do this on top of the
changes I pointed out during review. For example patch #6 is exposing
pin handle which is going to change, etc (there, I put a note).

First 5 patches are just needed dependencies and you can squash them
into your patch/patches. Last two patches should go in separatelly.

Please note that the patch #6 is replacing the need to pass the rclk
device during pin registration by putting a link between netdev and dpll
pin.

Please merge this into your dpll patchset and include it in the next
RFC. Thanks!

Jiri Pirko (7):
  dpll: make ops function args const
  dpll: allow to call device register multiple times
  dpll: introduce a helper to get first dpll ref and use it
  dpll: allow to call pin register multiple times
  dpll: export dpll_pin_notify()
  netdev: expose DPLL pin handle for netdevice
  mlx5: Implement SyncE support using DPLL infrastructure

 drivers/dpll/dpll_core.c                      | 334 ++++++++++----
 drivers/dpll/dpll_core.h                      |  25 +-
 drivers/dpll/dpll_netlink.c                   | 161 ++++---
 drivers/dpll/dpll_netlink.h                   |   3 -
 drivers/net/ethernet/intel/ice/ice_dpll.c     |  22 +-
 .../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    | 429 ++++++++++++++++++
 drivers/ptp/ptp_ocp.c                         |   2 +-
 include/linux/dpll.h                          |  46 +-
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 include/linux/netdevice.h                     |   7 +
 include/uapi/linux/if_link.h                  |   2 +
 net/core/dev.c                                |  20 +
 net/core/rtnetlink.c                          |  38 ++
 17 files changed, 1008 insertions(+), 170 deletions(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c

-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 1/7] dpll: make ops function args const
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Make ops args passed over *_register() functions const.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c | 25 ++++++++++++-------------
 include/linux/dpll.h     |  9 +++++----
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index d478ab5e2001..7f8442b73fd8 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -89,7 +89,7 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
  */
 static int
 dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
-		    struct dpll_pin_ops *ops, void *priv)
+		    const struct dpll_pin_ops *ops, void *priv)
 {
 	struct dpll_pin_ref *ref;
 	unsigned long i;
@@ -189,7 +189,7 @@ dpll_xa_ref_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
  */
 static int
 dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
-		     struct dpll_pin_ops *ops, void *priv)
+		     const struct dpll_pin_ops *ops, void *priv)
 {
 	struct dpll_pin_ref *ref;
 	unsigned long i;
@@ -385,8 +385,9 @@ EXPORT_SYMBOL_GPL(dpll_device_put);
  * * 0 on success
  * * -EINVAL on failure
  */
-int dpll_device_register(struct dpll_device *dpll, struct dpll_device_ops *ops,
-			 void *priv, struct device *owner)
+int dpll_device_register(struct dpll_device *dpll,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
 {
 	if (WARN_ON(!ops || !owner))
 		return -EINVAL;
@@ -551,7 +552,7 @@ EXPORT_SYMBOL_GPL(dpll_pin_put);
 
 static int
 __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		    struct dpll_pin_ops *ops, void *priv,
+		    const struct dpll_pin_ops *ops, void *priv,
 		    const char *rclk_device_name)
 {
 	int ret;
@@ -592,10 +593,9 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * * -EINVAL - missing dpll or pin
  * * -ENOMEM - failed to allocate memory
  */
-int
-dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		  struct dpll_pin_ops *ops, void *priv,
-		  struct device *rclk_device)
+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;
@@ -661,10 +661,9 @@ EXPORT_SYMBOL_GPL(dpll_pin_unregister);
  * * -ENOMEM failed allocation
  * * -EPERM if parent is not allowed
  */
-int
-dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
-			 struct dpll_pin_ops *ops, void *priv,
-			 struct device *rclk_device)
+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;
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 2e516d91c343..496358df83a9 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -123,8 +123,9 @@ void dpll_device_put(struct dpll_device *dpll);
  * @owner: device struct of the owner
  *
  */
-int dpll_device_register(struct dpll_device *dpll, struct dpll_device_ops *ops,
-			 void *priv, struct device *owner);
+int dpll_device_register(struct dpll_device *dpll,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
 
 /**
  * dpll_device_unregister - deregister registered dpll
@@ -200,7 +201,7 @@ struct dpll_pin
  * * -EBUSY - couldn't allocate id for a pin.
  */
 int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		      struct dpll_pin_ops *ops, void *priv,
+		      const struct dpll_pin_ops *ops, void *priv,
 		      struct device *rclk_device);
 
 /**
@@ -244,7 +245,7 @@ void dpll_pin_put(struct dpll_pin *pin);
  * * -EEXIST - pin already registered with this parent pin,
  */
 int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
-			     struct dpll_pin_ops *ops, void *priv,
+			     const struct dpll_pin_ops *ops, void *priv,
 			     struct device *rclk_device);
 
 /**
-- 
2.39.0


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

* [patch dpll-rfc 1/7] dpll: make ops function args const
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Make ops args passed over *_register() functions const.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c | 25 ++++++++++++-------------
 include/linux/dpll.h     |  9 +++++----
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index d478ab5e2001..7f8442b73fd8 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -89,7 +89,7 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
  */
 static int
 dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
-		    struct dpll_pin_ops *ops, void *priv)
+		    const struct dpll_pin_ops *ops, void *priv)
 {
 	struct dpll_pin_ref *ref;
 	unsigned long i;
@@ -189,7 +189,7 @@ dpll_xa_ref_pin_find(struct xarray *xa_pins, const struct dpll_pin *pin)
  */
 static int
 dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
-		     struct dpll_pin_ops *ops, void *priv)
+		     const struct dpll_pin_ops *ops, void *priv)
 {
 	struct dpll_pin_ref *ref;
 	unsigned long i;
@@ -385,8 +385,9 @@ EXPORT_SYMBOL_GPL(dpll_device_put);
  * * 0 on success
  * * -EINVAL on failure
  */
-int dpll_device_register(struct dpll_device *dpll, struct dpll_device_ops *ops,
-			 void *priv, struct device *owner)
+int dpll_device_register(struct dpll_device *dpll,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner)
 {
 	if (WARN_ON(!ops || !owner))
 		return -EINVAL;
@@ -551,7 +552,7 @@ EXPORT_SYMBOL_GPL(dpll_pin_put);
 
 static int
 __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		    struct dpll_pin_ops *ops, void *priv,
+		    const struct dpll_pin_ops *ops, void *priv,
 		    const char *rclk_device_name)
 {
 	int ret;
@@ -592,10 +593,9 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * * -EINVAL - missing dpll or pin
  * * -ENOMEM - failed to allocate memory
  */
-int
-dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		  struct dpll_pin_ops *ops, void *priv,
-		  struct device *rclk_device)
+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;
@@ -661,10 +661,9 @@ EXPORT_SYMBOL_GPL(dpll_pin_unregister);
  * * -ENOMEM failed allocation
  * * -EPERM if parent is not allowed
  */
-int
-dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
-			 struct dpll_pin_ops *ops, void *priv,
-			 struct device *rclk_device)
+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;
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 2e516d91c343..496358df83a9 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -123,8 +123,9 @@ void dpll_device_put(struct dpll_device *dpll);
  * @owner: device struct of the owner
  *
  */
-int dpll_device_register(struct dpll_device *dpll, struct dpll_device_ops *ops,
-			 void *priv, struct device *owner);
+int dpll_device_register(struct dpll_device *dpll,
+			 const struct dpll_device_ops *ops, void *priv,
+			 struct device *owner);
 
 /**
  * dpll_device_unregister - deregister registered dpll
@@ -200,7 +201,7 @@ struct dpll_pin
  * * -EBUSY - couldn't allocate id for a pin.
  */
 int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
-		      struct dpll_pin_ops *ops, void *priv,
+		      const struct dpll_pin_ops *ops, void *priv,
 		      struct device *rclk_device);
 
 /**
@@ -244,7 +245,7 @@ void dpll_pin_put(struct dpll_pin *pin);
  * * -EEXIST - pin already registered with this parent pin,
  */
 int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
-			     struct dpll_pin_ops *ops, void *priv,
+			     const struct dpll_pin_ops *ops, void *priv,
 			     struct device *rclk_device);
 
 /**
-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 2/7] dpll: allow to call device register multiple times
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Some devices allow to control a single dpll instance over multiple
channels. In case of mlx5 for example, one dpll could be controlled over
multiple PFs that reside on a single ASIC. These are equal. Allow each
to register/unregister dpll device. Use the first ops and priv always
as the is no difference in between those and the rest.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c                  | 85 +++++++++++++++++++++--
 drivers/dpll/dpll_core.h                  | 11 ++-
 drivers/dpll/dpll_netlink.c               | 29 ++++----
 drivers/net/ethernet/intel/ice/ice_dpll.c |  4 +-
 drivers/ptp/ptp_ocp.c                     |  2 +-
 include/linux/dpll.h                      |  5 +-
 6 files changed, 113 insertions(+), 23 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 7f8442b73fd8..6e50216a636a 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -293,6 +293,7 @@ dpll_device_alloc(const u64 clock_id, u32 dev_driver_id,
 	if (!dpll)
 		return ERR_PTR(-ENOMEM);
 	refcount_set(&dpll->refcount, 1);
+	INIT_LIST_HEAD(&dpll->registration_list);
 	dpll->dev.class = &dpll_class;
 	dpll->dev_driver_id = dev_driver_id;
 	dpll->clock_id = clock_id;
@@ -366,12 +367,26 @@ void dpll_device_put(struct dpll_device *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_device_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
@@ -389,19 +404,39 @@ int dpll_device_register(struct dpll_device *dpll,
 			 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;
+
 	mutex_lock(&dpll_device_xa_lock);
-	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
 		mutex_unlock(&dpll_device_xa_lock);
 		return -EEXIST;
 	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+
 	dpll->dev.bus = owner->bus;
 	dpll->parent = owner;
-	dpll->ops = ops;
 	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
 		     dpll->dev_driver_id);
-	dpll->priv = priv;
+
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return 0;
+	}
+
 	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
 	mutex_unlock(&dpll_device_xa_lock);
 	dpll_notify_device_create(dpll);
@@ -413,14 +448,32 @@ 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)
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
 {
+	struct dpll_device_registration *reg;
+
 	mutex_lock(&dpll_device_xa_lock);
 	ASSERT_DPLL_REGISTERED(dpll);
+
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return;
+	}
 	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
 	mutex_unlock(&dpll_device_xa_lock);
 	dpll_notify_device_delete(dpll);
@@ -760,6 +813,17 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
 	return NULL;
 }
 
+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
@@ -768,10 +832,21 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
  */
 void *dpll_priv(const struct dpll_device *dpll)
 {
-	return dpll->priv;
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_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;
+}
+
 /**
  * dpll_pin_on_dpll_priv - get the dpll device private owner data
  * @dpll:	registered dpll pointer
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 876b6ac6f3a0..21ba31621b44 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -7,11 +7,18 @@
 #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
@@ -34,9 +41,8 @@ struct dpll_device {
 	struct device dev;
 	struct device *parent;
 	struct module *module;
-	struct dpll_device_ops *ops;
 	enum dpll_type type;
-	void *priv;
+	struct list_head registration_list;
 	struct xarray pin_refs;
 	u64 clock_id;
 	unsigned long mode_supported_mask;
@@ -84,6 +90,7 @@ struct dpll_pin_ref {
 	refcount_t refcount;
 };
 
+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);
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index d2c699015215..430c009d0a71 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -18,7 +18,7 @@ static u32 dpll_pin_freq_value[] = {
 };
 
 static int
-dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
+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;
@@ -31,14 +31,15 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
 }
 
 static int
-dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll,
+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(!dpll->ops->mode_get))
+	if (WARN_ON(!ops->mode_get))
 		return -EOPNOTSUPP;
-	if (dpll->ops->mode_get(dpll, &mode, extack))
+	if (ops->mode_get(dpll, &mode, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_MODE, mode))
 		return -EMSGSIZE;
@@ -50,11 +51,12 @@ static int
 dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
 			    struct netlink_ext_ack *extack)
 {
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
 	u32 source_pin_idx;
 
-	if (!dpll->ops->source_pin_idx_get)
+	if (!ops->source_pin_idx_get)
 		return 0;
-	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
+	if (ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
 		return -EMSGSIZE;
@@ -66,11 +68,12 @@ 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(!dpll->ops->lock_status_get))
+	if (WARN_ON(!ops->lock_status_get))
 		return -EOPNOTSUPP;
-	if (dpll->ops->lock_status_get(dpll, &status, extack))
+	if (ops->lock_status_get(dpll, &status, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
 		return -EMSGSIZE;
@@ -82,11 +85,12 @@ 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 (!dpll->ops->temp_get)
+	if (!ops->temp_get)
 		return -EOPNOTSUPP;
-	if (dpll->ops->temp_get(dpll, &temp, extack))
+	if (ops->temp_get(dpll, &temp, extack))
 		return -EFAULT;
 	if (nla_put_s32(msg, DPLL_A_TEMP, temp))
 		return -EMSGSIZE;
@@ -686,6 +690,7 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 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;
@@ -696,9 +701,9 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
 		case DPLL_A_MODE:
 			mode = nla_get_u8(attr);
 
-			if (!dpll->ops || !dpll->ops->mode_set)
+			if (!ops->mode_set)
 				return -EOPNOTSUPP;
-			ret = dpll->ops->mode_set(dpll, mode, info->extack);
+			ret = ops->mode_set(dpll, mode, info->extack);
 			if (ret)
 				return ret;
 			break;
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 87572ecc21e4..532ad7314f49 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1551,7 +1551,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 	if (dp->dpll) {
 		mutex_lock(&pf->dplls.lock);
 		if (cgu)
-			dpll_device_unregister(dp->dpll);
+			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");
@@ -1560,7 +1560,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 	if (de->dpll) {
 		mutex_lock(&pf->dplls.lock);
 		if (cgu)
-			dpll_device_unregister(de->dpll);
+			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");
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index cc840d0e3265..5e7fceae2a6a 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4431,7 +4431,7 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
 
-	dpll_device_unregister(bp->dpll);
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
 	dpll_device_put(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 496358df83a9..09863d66a44c 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -130,11 +130,14 @@ int dpll_device_register(struct dpll_device *dpll,
 /**
  * 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);
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
 
 /**
  * dpll_priv - get dpll private data
-- 
2.39.0


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

* [patch dpll-rfc 2/7] dpll: allow to call device register multiple times
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Some devices allow to control a single dpll instance over multiple
channels. In case of mlx5 for example, one dpll could be controlled over
multiple PFs that reside on a single ASIC. These are equal. Allow each
to register/unregister dpll device. Use the first ops and priv always
as the is no difference in between those and the rest.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c                  | 85 +++++++++++++++++++++--
 drivers/dpll/dpll_core.h                  | 11 ++-
 drivers/dpll/dpll_netlink.c               | 29 ++++----
 drivers/net/ethernet/intel/ice/ice_dpll.c |  4 +-
 drivers/ptp/ptp_ocp.c                     |  2 +-
 include/linux/dpll.h                      |  5 +-
 6 files changed, 113 insertions(+), 23 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 7f8442b73fd8..6e50216a636a 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -293,6 +293,7 @@ dpll_device_alloc(const u64 clock_id, u32 dev_driver_id,
 	if (!dpll)
 		return ERR_PTR(-ENOMEM);
 	refcount_set(&dpll->refcount, 1);
+	INIT_LIST_HEAD(&dpll->registration_list);
 	dpll->dev.class = &dpll_class;
 	dpll->dev_driver_id = dev_driver_id;
 	dpll->clock_id = clock_id;
@@ -366,12 +367,26 @@ void dpll_device_put(struct dpll_device *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_device_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
@@ -389,19 +404,39 @@ int dpll_device_register(struct dpll_device *dpll,
 			 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;
+
 	mutex_lock(&dpll_device_xa_lock);
-	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
 		mutex_unlock(&dpll_device_xa_lock);
 		return -EEXIST;
 	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+
 	dpll->dev.bus = owner->bus;
 	dpll->parent = owner;
-	dpll->ops = ops;
 	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
 		     dpll->dev_driver_id);
-	dpll->priv = priv;
+
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return 0;
+	}
+
 	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
 	mutex_unlock(&dpll_device_xa_lock);
 	dpll_notify_device_create(dpll);
@@ -413,14 +448,32 @@ 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)
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
 {
+	struct dpll_device_registration *reg;
+
 	mutex_lock(&dpll_device_xa_lock);
 	ASSERT_DPLL_REGISTERED(dpll);
+
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_device_xa_lock);
+		return;
+	}
 	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
 	mutex_unlock(&dpll_device_xa_lock);
 	dpll_notify_device_delete(dpll);
@@ -760,6 +813,17 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
 	return NULL;
 }
 
+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
@@ -768,10 +832,21 @@ struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
  */
 void *dpll_priv(const struct dpll_device *dpll)
 {
-	return dpll->priv;
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first((struct dpll_device *) dpll);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_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;
+}
+
 /**
  * dpll_pin_on_dpll_priv - get the dpll device private owner data
  * @dpll:	registered dpll pointer
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 876b6ac6f3a0..21ba31621b44 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -7,11 +7,18 @@
 #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
@@ -34,9 +41,8 @@ struct dpll_device {
 	struct device dev;
 	struct device *parent;
 	struct module *module;
-	struct dpll_device_ops *ops;
 	enum dpll_type type;
-	void *priv;
+	struct list_head registration_list;
 	struct xarray pin_refs;
 	u64 clock_id;
 	unsigned long mode_supported_mask;
@@ -84,6 +90,7 @@ struct dpll_pin_ref {
 	refcount_t refcount;
 };
 
+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);
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index d2c699015215..430c009d0a71 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -18,7 +18,7 @@ static u32 dpll_pin_freq_value[] = {
 };
 
 static int
-dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
+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;
@@ -31,14 +31,15 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device *dpll)
 }
 
 static int
-dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll,
+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(!dpll->ops->mode_get))
+	if (WARN_ON(!ops->mode_get))
 		return -EOPNOTSUPP;
-	if (dpll->ops->mode_get(dpll, &mode, extack))
+	if (ops->mode_get(dpll, &mode, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_MODE, mode))
 		return -EMSGSIZE;
@@ -50,11 +51,12 @@ static int
 dpll_msg_add_source_pin_idx(struct sk_buff *msg, struct dpll_device *dpll,
 			    struct netlink_ext_ack *extack)
 {
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
 	u32 source_pin_idx;
 
-	if (!dpll->ops->source_pin_idx_get)
+	if (!ops->source_pin_idx_get)
 		return 0;
-	if (dpll->ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
+	if (ops->source_pin_idx_get(dpll, &source_pin_idx, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_SOURCE_PIN_IDX, source_pin_idx))
 		return -EMSGSIZE;
@@ -66,11 +68,12 @@ 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(!dpll->ops->lock_status_get))
+	if (WARN_ON(!ops->lock_status_get))
 		return -EOPNOTSUPP;
-	if (dpll->ops->lock_status_get(dpll, &status, extack))
+	if (ops->lock_status_get(dpll, &status, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
 		return -EMSGSIZE;
@@ -82,11 +85,12 @@ 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 (!dpll->ops->temp_get)
+	if (!ops->temp_get)
 		return -EOPNOTSUPP;
-	if (dpll->ops->temp_get(dpll, &temp, extack))
+	if (ops->temp_get(dpll, &temp, extack))
 		return -EFAULT;
 	if (nla_put_s32(msg, DPLL_A_TEMP, temp))
 		return -EMSGSIZE;
@@ -686,6 +690,7 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 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;
@@ -696,9 +701,9 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
 		case DPLL_A_MODE:
 			mode = nla_get_u8(attr);
 
-			if (!dpll->ops || !dpll->ops->mode_set)
+			if (!ops->mode_set)
 				return -EOPNOTSUPP;
-			ret = dpll->ops->mode_set(dpll, mode, info->extack);
+			ret = ops->mode_set(dpll, mode, info->extack);
 			if (ret)
 				return ret;
 			break;
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 87572ecc21e4..532ad7314f49 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1551,7 +1551,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 	if (dp->dpll) {
 		mutex_lock(&pf->dplls.lock);
 		if (cgu)
-			dpll_device_unregister(dp->dpll);
+			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");
@@ -1560,7 +1560,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 	if (de->dpll) {
 		mutex_lock(&pf->dplls.lock);
 		if (cgu)
-			dpll_device_unregister(de->dpll);
+			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");
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index cc840d0e3265..5e7fceae2a6a 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4431,7 +4431,7 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
 
-	dpll_device_unregister(bp->dpll);
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
 	dpll_device_put(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 496358df83a9..09863d66a44c 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -130,11 +130,14 @@ int dpll_device_register(struct dpll_device *dpll,
 /**
  * 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);
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
 
 /**
  * dpll_priv - get dpll private data
-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 3/7] dpll: introduce a helper to get first dpll ref and use it
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

No need to iterate xaarray in dpll_msg_add_pin_direction() and
dpll_msg_add_pin_freq(). Just introduce a helper to get the first dpll
reference and use that.

Add a check for ops not being null in pin_register() function, not only
for sake of this change, but also for the sake of existing code relying
on it.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c    | 12 ++++++++++++
 drivers/dpll/dpll_core.h    |  1 +
 drivers/dpll/dpll_netlink.c | 34 ++++++++++++----------------------
 3 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 6e50216a636a..2f788d2349fe 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -267,6 +267,15 @@ dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
 	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
@@ -610,6 +619,9 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 {
 	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)
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 21ba31621b44..636dd4c6710e 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -99,6 +99,7 @@ struct dpll_pin_ref *
 dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
 struct dpll_pin_ref *
 dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
+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_device_xa_lock;
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 430c009d0a71..41e2f8a90aeb 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -134,18 +134,11 @@ dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, const struct dpll_pin *pin,
 
 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)
 {
 	enum dpll_pin_direction direction;
-	struct dpll_pin_ref *ref;
-	unsigned long i;
 
-	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
-		if (ref && ref->ops && ref->dpll)
-			break;
-	}
-	if (!ref || !ref->ops || !ref->dpll)
-		return -ENODEV;
 	if (!ref->ops->direction_get)
 		return -EOPNOTSUPP;
 	if (ref->ops->direction_get(pin, ref->dpll, &direction, extack))
@@ -158,19 +151,12 @@ dpll_msg_add_pin_direction(struct sk_buff *msg, const struct dpll_pin *pin,
 
 static int
 dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
-		      struct netlink_ext_ack *extack, bool dump_any_freq)
+		      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack,
+		      bool dump_any_freq)
 {
 	enum dpll_pin_freq_supp fs;
-	struct dpll_pin_ref *ref;
-	unsigned long i;
 	u32 freq;
 
-	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
-		if (ref && ref->ops && ref->dpll)
-			break;
-	}
-	if (!ref || !ref->ops || !ref->dpll)
-		return -ENODEV;
 	if (!ref->ops->frequency_get)
 		return -EOPNOTSUPP;
 	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
@@ -325,10 +311,11 @@ dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 		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, extack);
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
 	if (ret)
 		return ret;
-	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
 	if (ret && ret != -EOPNOTSUPP)
 		return ret;
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
@@ -355,6 +342,7 @@ static int
 __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 			struct netlink_ext_ack *extack, bool dump_dpll)
 {
+	struct dpll_pin_ref *ref;
 	int ret;
 
 	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
@@ -363,10 +351,11 @@ __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
 		return -EMSGSIZE;
-	ret = dpll_msg_add_pin_direction(msg, pin, extack);
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
 	if (ret)
 		return ret;
-	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
 	if (ret && ret != -EOPNOTSUPP)
 		return ret;
 	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
@@ -920,7 +909,8 @@ dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
 		ret = dpll_msg_add_temp(msg, dpll, NULL);
 		break;
 	case DPLL_A_PIN_FREQUENCY:
-		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
 		break;
 	case DPLL_A_PIN_PRIO:
 		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
-- 
2.39.0


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

* [patch dpll-rfc 3/7] dpll: introduce a helper to get first dpll ref and use it
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

No need to iterate xaarray in dpll_msg_add_pin_direction() and
dpll_msg_add_pin_freq(). Just introduce a helper to get the first dpll
reference and use that.

Add a check for ops not being null in pin_register() function, not only
for sake of this change, but also for the sake of existing code relying
on it.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c    | 12 ++++++++++++
 drivers/dpll/dpll_core.h    |  1 +
 drivers/dpll/dpll_netlink.c | 34 ++++++++++++----------------------
 3 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 6e50216a636a..2f788d2349fe 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -267,6 +267,15 @@ dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
 	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
@@ -610,6 +619,9 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 {
 	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)
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 21ba31621b44..636dd4c6710e 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -99,6 +99,7 @@ struct dpll_pin_ref *
 dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
 struct dpll_pin_ref *
 dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll);
+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_device_xa_lock;
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 430c009d0a71..41e2f8a90aeb 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -134,18 +134,11 @@ dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, const struct dpll_pin *pin,
 
 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)
 {
 	enum dpll_pin_direction direction;
-	struct dpll_pin_ref *ref;
-	unsigned long i;
 
-	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
-		if (ref && ref->ops && ref->dpll)
-			break;
-	}
-	if (!ref || !ref->ops || !ref->dpll)
-		return -ENODEV;
 	if (!ref->ops->direction_get)
 		return -EOPNOTSUPP;
 	if (ref->ops->direction_get(pin, ref->dpll, &direction, extack))
@@ -158,19 +151,12 @@ dpll_msg_add_pin_direction(struct sk_buff *msg, const struct dpll_pin *pin,
 
 static int
 dpll_msg_add_pin_freq(struct sk_buff *msg, const struct dpll_pin *pin,
-		      struct netlink_ext_ack *extack, bool dump_any_freq)
+		      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack,
+		      bool dump_any_freq)
 {
 	enum dpll_pin_freq_supp fs;
-	struct dpll_pin_ref *ref;
-	unsigned long i;
 	u32 freq;
 
-	xa_for_each((struct xarray *)&pin->dpll_refs, i, ref) {
-		if (ref && ref->ops && ref->dpll)
-			break;
-	}
-	if (!ref || !ref->ops || !ref->dpll)
-		return -ENODEV;
 	if (!ref->ops->frequency_get)
 		return -EOPNOTSUPP;
 	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
@@ -325,10 +311,11 @@ dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 		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, extack);
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
 	if (ret)
 		return ret;
-	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
 	if (ret && ret != -EOPNOTSUPP)
 		return ret;
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
@@ -355,6 +342,7 @@ static int
 __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 			struct netlink_ext_ack *extack, bool dump_dpll)
 {
+	struct dpll_pin_ref *ref;
 	int ret;
 
 	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
@@ -363,10 +351,11 @@ __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
 		return -EMSGSIZE;
-	ret = dpll_msg_add_pin_direction(msg, pin, extack);
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
 	if (ret)
 		return ret;
-	ret = dpll_msg_add_pin_freq(msg, pin, extack, true);
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
 	if (ret && ret != -EOPNOTSUPP)
 		return ret;
 	ret = dpll_msg_add_pins_on_pin(msg, pin, extack);
@@ -920,7 +909,8 @@ dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
 		ret = dpll_msg_add_temp(msg, dpll, NULL);
 		break;
 	case DPLL_A_PIN_FREQUENCY:
-		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
+		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
+		ret = dpll_msg_add_pin_freq(msg, pin, ref, NULL, false);
 		break;
 	case DPLL_A_PIN_PRIO:
 		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 4/7] dpll: allow to call pin register multiple times
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Some devices allow to control a single dpll pin instance over multiple
channels. In case of mlx5 for example, one dpll pin could be controlled
over multiple PFs that reside on a single ASIC. These are equal.
Allow each to register/unregister dpll pin. Use the first ops and
priv always as the is no difference in between those and the rest.
Replace the existing reference counting by the list of registrations as
they serve the same purpose.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c                  | 212 +++++++++++++++-------
 drivers/dpll/dpll_core.h                  |  13 +-
 drivers/dpll/dpll_netlink.c               |  69 ++++---
 drivers/net/ethernet/intel/ice/ice_dpll.c |  18 +-
 include/linux/dpll.h                      |   8 +-
 5 files changed, 220 insertions(+), 100 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 2f788d2349fe..dfff3e07fe43 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -73,6 +73,19 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
 	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
@@ -80,8 +93,8 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
  * @ops: ops for a pin
  * @priv: pointer to private data of owner
  *
- * Allocate and create reference of a pin or increase refcount on existing pin
- * reference on given xarray.
+ * 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
@@ -91,31 +104,48 @@ 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;
 	u32 idx;
 	int ret;
 
 	xa_for_each(xa_pins, i, ref) {
-		if (ref->pin == pin) {
-			refcount_inc(&ref->refcount);
-			return 0;
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg)
+			return -EEXIST;
+		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_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
 		}
 	}
 
-	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
-	if (!ref)
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists)
+			kfree(ref);
 		return -ENOMEM;
-	ref->pin = pin;
-	ref->ops = ops;
-	ref->priv = priv;
-	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
-	if (!ret)
-		refcount_set(&ref->refcount, 1);
-	else
-		kfree(ref);
+	}
+	reg->ops = ops;
+	reg->priv = priv;
 
-	return ret;
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
 }
 
 /**
@@ -124,25 +154,31 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
  * @pin: pointer to a pin
  *
  * Decrement refcount of existing pin reference on given xarray.
- * If all references are dropped, delete the reference and free its memory.
+ * 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)
+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) {
-			if (refcount_dec_and_test(&ref->refcount)) {
-				xa_erase(xa_pins, i);
-				kfree(ref);
-			}
-			return 0;
-		}
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return -EINVAL;
+		list_del(&reg->list);
+		kfree(reg);
+		xa_erase(xa_pins, i);
+		WARN_ON(!list_empty(&ref->registration_list));
+		kfree(ref);
+		return 0;
 	}
 
 	return -EINVAL;
@@ -191,30 +227,48 @@ 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;
 	u32 idx;
 	int ret;
 
 	xa_for_each(xa_dplls, i, ref) {
-		if (ref->dpll == dpll) {
-			refcount_inc(&ref->refcount);
-			return 0;
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg)
+			return -EEXIST;
+		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_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
 		}
 	}
-	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
-	if (!ref)
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists)
+			kfree(ref);
 		return -ENOMEM;
-	ref->dpll = dpll;
-	ref->ops = ops;
-	ref->priv = priv;
-	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
-	if (!ret)
-		refcount_set(&ref->refcount, 1);
-	else
-		kfree(ref);
+	}
+	reg->ops = ops;
+	reg->priv = priv;
 
-	return ret;
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
 }
 
 /**
@@ -226,19 +280,25 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
  * 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)
+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) {
-			if (refcount_dec_and_test(&ref->refcount)) {
-				xa_erase(xa_dplls, i);
-				kfree(ref);
-			}
-			break;
-		}
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return;
+		list_del(&reg->list);
+		kfree(reg);
+		xa_erase(xa_dplls, i);
+		WARN_ON(!list_empty(&ref->registration_list));
+		kfree(ref);
+		return;
 	}
 }
 
@@ -639,7 +699,7 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 	return ret;
 
 ref_pin_del:
-	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
 rclk_free:
 	kfree(pin->rclk_dev_name);
 	return ret;
@@ -681,27 +741,31 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 EXPORT_SYMBOL_GPL(dpll_pin_register);
 
 static void
-__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+__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);
-	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
+	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 ops
+ * @priv: pointer to private information of owner
  *
  * Note: It does not free the memory
  */
-int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+int 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 -ENOENT;
 
 	mutex_lock(&dpll_device_xa_lock);
 	mutex_lock(&dpll_pin_xa_lock);
-	__dpll_pin_unregister(dpll, pin);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
 	mutex_unlock(&dpll_pin_xa_lock);
 	mutex_unlock(&dpll_device_xa_lock);
 
@@ -763,12 +827,12 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
 	xa_for_each(&parent->dpll_refs, i, ref) {
 		if (i < stop) {
 			mutex_lock(&dpll_device_xa_lock);
-			__dpll_pin_unregister(ref->dpll, pin);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
 			mutex_unlock(&dpll_device_xa_lock);
 		}
 	}
 	refcount_dec(&pin->refcount);
-	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
 unlock:
 	mutex_unlock(&dpll_pin_xa_lock);
 	return ret;
@@ -779,20 +843,23 @@ 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)
+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_device_xa_lock);
 	mutex_lock(&dpll_pin_xa_lock);
-	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	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);
+		__dpll_pin_unregister(ref->dpll, pin, ops, priv);
 		dpll_pin_parent_notify(ref->dpll, pin, parent,
 				       DPLL_A_PIN_IDX);
 	}
@@ -859,6 +926,17 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *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
@@ -869,13 +947,14 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
 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 = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
 	if (!ref)
 		return NULL;
-
-	return ref->priv;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_priv);
 
@@ -889,16 +968,25 @@ EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_priv);
 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 = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
 	if (!ref)
 		return NULL;
-
-	return ref->priv;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_pin_on_pin_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;
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 636dd4c6710e..2ab2d9e0a3cd 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -73,27 +73,30 @@ struct dpll_pin {
 	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
- * @ops:		ops for a dpll pin
- * @priv:		pointer to private information of owner
  **/
 struct dpll_pin_ref {
 	union {
 		struct dpll_device *dpll;
 		struct dpll_pin *pin;
 	};
-	struct dpll_pin_ops *ops;
-	void *priv;
-	refcount_t refcount;
+	struct list_head registration_list;
 };
 
 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 *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
 struct dpll_pin_ref *
 dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 41e2f8a90aeb..125dc3c7e643 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -103,11 +103,12 @@ 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);
 	u32 prio;
 
-	if (!ref->ops->prio_get)
+	if (!ops->prio_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->prio_get(pin, ref->dpll, &prio, extack))
+	if (ops->prio_get(pin, ref->dpll, &prio, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
 		return -EMSGSIZE;
@@ -120,11 +121,12 @@ 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);
 	enum dpll_pin_state state;
 
-	if (!ref->ops->state_on_dpll_get)
+	if (!ops->state_on_dpll_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_dpll_get(pin, ref->dpll, &state, extack))
+	if (ops->state_on_dpll_get(pin, ref->dpll, &state, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
 		return -EMSGSIZE;
@@ -137,11 +139,12 @@ 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);
 	enum dpll_pin_direction direction;
 
-	if (!ref->ops->direction_get)
+	if (!ops->direction_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->direction_get(pin, ref->dpll, &direction, extack))
+	if (ops->direction_get(pin, ref->dpll, &direction, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
 		return -EMSGSIZE;
@@ -154,12 +157,13 @@ 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_any_freq)
 {
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
 	enum dpll_pin_freq_supp fs;
 	u32 freq;
 
-	if (!ref->ops->frequency_get)
+	if (!ops->frequency_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
+	if (ops->frequency_get(pin, ref->dpll, &freq, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
 		return -EMSGSIZE;
@@ -196,10 +200,12 @@ dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
 	int ret;
 
 	xa_for_each(&pin->parent_refs, index, ref_parent) {
-		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref_parent);
+
+		if (WARN_ON(!ops->state_on_pin_get))
 			return -EFAULT;
-		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
-							&state, extack);
+		ret = ops->state_on_pin_get(pin, ref_parent->pin,
+					    &state, extack);
 		if (ret)
 			return -EFAULT;
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
@@ -235,10 +241,11 @@ dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
 	int ret;
 
 	xa_for_each(&pin->parent_refs, index, ref) {
-		if (WARN_ON(!ref->ops->state_on_pin_get))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		if (WARN_ON(!ops->state_on_pin_get))
 			return -EFAULT;
-		ret = ref->ops->state_on_pin_get(pin, ref->pin, &state,
-						 extack);
+		ret = ops->state_on_pin_get(pin, ref->pin, &state, extack);
 		if (ret)
 			return -EFAULT;
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
@@ -452,7 +459,9 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EINVAL;
 
 	xa_for_each(&pin->dpll_refs, i, ref) {
-		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		ret = ops->frequency_set(pin, ref->dpll, freq, extack);
 		if (ret)
 			return -EFAULT;
 		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
@@ -466,6 +475,7 @@ 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 *ref;
 	struct dpll_pin *parent;
 
@@ -477,9 +487,10 @@ dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
 	if (!ref)
 		return -EINVAL;
-	if (!ref->ops || !ref->ops->state_on_pin_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_pin_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
+	if (ops->state_on_pin_set(pin, parent, state, extack))
 		return -EFAULT;
 	dpll_pin_parent_notify(dpll, pin, parent, DPLL_A_PIN_STATE);
 
@@ -491,6 +502,7 @@ 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))
@@ -498,9 +510,10 @@ dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
 	if (!ref)
 		return -EFAULT;
-	if (!ref->ops || !ref->ops->state_on_dpll_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_dpll_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
+	if (ops->state_on_dpll_set(pin, ref->dpll, state, extack))
 		return -EINVAL;
 	dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_STATE);
 
@@ -511,6 +524,7 @@ 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);
 
@@ -519,9 +533,10 @@ dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
 	if (!ref)
 		return -EFAULT;
-	if (!ref->ops || !ref->ops->prio_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->prio_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->prio_set(pin, dpll, prio, extack))
+	if (ops->prio_set(pin, dpll, prio, extack))
 		return -EINVAL;
 	dpll_pin_notify(dpll, pin, DPLL_A_PIN_PRIO);
 
@@ -540,7 +555,9 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EOPNOTSUPP;
 
 	xa_for_each(&pin->dpll_refs, i, ref) {
-		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		if (ops->direction_set(pin, ref->dpll, direction, extack))
 			return -EFAULT;
 		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
 	}
@@ -920,13 +937,15 @@ dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
 		break;
 	case DPLL_A_PIN_STATE:
 		if (parent) {
+			const struct dpll_pin_ops *ops;
+
 			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
 			if (!ref)
 				return -EFAULT;
-			if (!ref->ops || !ref->ops->state_on_pin_get)
+			ops = dpll_pin_ops(ref);
+			if (!ops->state_on_pin_get)
 				return -EOPNOTSUPP;
-			ret = ref->ops->state_on_pin_get(pin, parent, &state,
-							 NULL);
+			ret = ops->state_on_pin_get(pin, parent, &state, NULL);
 			if (ret)
 				return ret;
 			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 532ad7314f49..d39292ad102f 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1194,7 +1194,8 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
 		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
 		if (!parent)
 			continue;
-		dpll_pin_on_pin_unregister(parent, rclk->pin);
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, pf);
 	}
 	dpll_pin_put(rclk->pin);
 	rclk->pin = NULL;
@@ -1202,6 +1203,7 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
 
 /**
  * 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
@@ -1215,7 +1217,7 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
  * * positive - number of errors encounterd on pin's deregistration.
  */
 static int
-ice_dpll_release_pins(struct dpll_device *dpll_eec,
+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, bool cgu)
 {
@@ -1226,12 +1228,16 @@ ice_dpll_release_pins(struct dpll_device *dpll_eec,
 
 		if (p && !IS_ERR_OR_NULL(p->pin)) {
 			if (cgu && dpll_eec) {
-				ret = dpll_pin_unregister(dpll_eec, p->pin);
+				ret = dpll_pin_unregister(dpll_eec, p->pin,
+							  &ice_dpll_source_ops,
+							  pf);
 				if (ret)
 					err++;
 			}
 			if (cgu && dpll_pps) {
-				ret = dpll_pin_unregister(dpll_pps, p->pin);
+				ret = dpll_pin_unregister(dpll_pps, p->pin,
+							  &ice_dpll_source_ops,
+							  pf);
 				if (ret)
 					err++;
 			}
@@ -1532,7 +1538,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 
 	mutex_lock(&pf->dplls.lock);
 	ice_dpll_release_rclk_pin(pf);
-	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+	ret = ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
 				    d->num_inputs, cgu);
 	mutex_unlock(&pf->dplls.lock);
 	if (ret)
@@ -1540,7 +1546,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 			 "source pins release dplls err=%d\n", ret);
 	if (cgu) {
 		mutex_lock(&pf->dplls.lock);
-		ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+		ret = ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
 					    d->num_outputs, cgu);
 		mutex_unlock(&pf->dplls.lock);
 		if (ret)
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 09863d66a44c..be5717e1da99 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -211,6 +211,8 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * 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.
  *
@@ -219,7 +221,8 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * * -ENXIO - given pin was not registered with this dpll device,
  * * -EINVAL - pin pointer is not valid.
  */
-int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
+int 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
@@ -268,7 +271,8 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *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);
+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
-- 
2.39.0


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

* [patch dpll-rfc 4/7] dpll: allow to call pin register multiple times
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Some devices allow to control a single dpll pin instance over multiple
channels. In case of mlx5 for example, one dpll pin could be controlled
over multiple PFs that reside on a single ASIC. These are equal.
Allow each to register/unregister dpll pin. Use the first ops and
priv always as the is no difference in between those and the rest.
Replace the existing reference counting by the list of registrations as
they serve the same purpose.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_core.c                  | 212 +++++++++++++++-------
 drivers/dpll/dpll_core.h                  |  13 +-
 drivers/dpll/dpll_netlink.c               |  69 ++++---
 drivers/net/ethernet/intel/ice/ice_dpll.c |  18 +-
 include/linux/dpll.h                      |   8 +-
 5 files changed, 220 insertions(+), 100 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 2f788d2349fe..dfff3e07fe43 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -73,6 +73,19 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
 	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
@@ -80,8 +93,8 @@ dpll_device_get_by_name(const char *bus_name, const char *device_name)
  * @ops: ops for a pin
  * @priv: pointer to private data of owner
  *
- * Allocate and create reference of a pin or increase refcount on existing pin
- * reference on given xarray.
+ * 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
@@ -91,31 +104,48 @@ 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;
 	u32 idx;
 	int ret;
 
 	xa_for_each(xa_pins, i, ref) {
-		if (ref->pin == pin) {
-			refcount_inc(&ref->refcount);
-			return 0;
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg)
+			return -EEXIST;
+		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_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
 		}
 	}
 
-	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
-	if (!ref)
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists)
+			kfree(ref);
 		return -ENOMEM;
-	ref->pin = pin;
-	ref->ops = ops;
-	ref->priv = priv;
-	ret = xa_alloc(xa_pins, &idx, ref, xa_limit_16b, GFP_KERNEL);
-	if (!ret)
-		refcount_set(&ref->refcount, 1);
-	else
-		kfree(ref);
+	}
+	reg->ops = ops;
+	reg->priv = priv;
 
-	return ret;
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
 }
 
 /**
@@ -124,25 +154,31 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
  * @pin: pointer to a pin
  *
  * Decrement refcount of existing pin reference on given xarray.
- * If all references are dropped, delete the reference and free its memory.
+ * 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)
+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) {
-			if (refcount_dec_and_test(&ref->refcount)) {
-				xa_erase(xa_pins, i);
-				kfree(ref);
-			}
-			return 0;
-		}
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return -EINVAL;
+		list_del(&reg->list);
+		kfree(reg);
+		xa_erase(xa_pins, i);
+		WARN_ON(!list_empty(&ref->registration_list));
+		kfree(ref);
+		return 0;
 	}
 
 	return -EINVAL;
@@ -191,30 +227,48 @@ 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;
 	u32 idx;
 	int ret;
 
 	xa_for_each(xa_dplls, i, ref) {
-		if (ref->dpll == dpll) {
-			refcount_inc(&ref->refcount);
-			return 0;
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg)
+			return -EEXIST;
+		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_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
 		}
 	}
-	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
-	if (!ref)
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists)
+			kfree(ref);
 		return -ENOMEM;
-	ref->dpll = dpll;
-	ref->ops = ops;
-	ref->priv = priv;
-	ret = xa_alloc(xa_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
-	if (!ret)
-		refcount_set(&ref->refcount, 1);
-	else
-		kfree(ref);
+	}
+	reg->ops = ops;
+	reg->priv = priv;
 
-	return ret;
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
 }
 
 /**
@@ -226,19 +280,25 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
  * 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)
+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) {
-			if (refcount_dec_and_test(&ref->refcount)) {
-				xa_erase(xa_dplls, i);
-				kfree(ref);
-			}
-			break;
-		}
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return;
+		list_del(&reg->list);
+		kfree(reg);
+		xa_erase(xa_dplls, i);
+		WARN_ON(!list_empty(&ref->registration_list));
+		kfree(ref);
+		return;
 	}
 }
 
@@ -639,7 +699,7 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 	return ret;
 
 ref_pin_del:
-	dpll_xa_ref_pin_del(&dpll->pin_refs, pin);
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
 rclk_free:
 	kfree(pin->rclk_dev_name);
 	return ret;
@@ -681,27 +741,31 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 EXPORT_SYMBOL_GPL(dpll_pin_register);
 
 static void
-__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+__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);
-	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll);
+	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 ops
+ * @priv: pointer to private information of owner
  *
  * Note: It does not free the memory
  */
-int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin)
+int 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 -ENOENT;
 
 	mutex_lock(&dpll_device_xa_lock);
 	mutex_lock(&dpll_pin_xa_lock);
-	__dpll_pin_unregister(dpll, pin);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
 	mutex_unlock(&dpll_pin_xa_lock);
 	mutex_unlock(&dpll_device_xa_lock);
 
@@ -763,12 +827,12 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
 	xa_for_each(&parent->dpll_refs, i, ref) {
 		if (i < stop) {
 			mutex_lock(&dpll_device_xa_lock);
-			__dpll_pin_unregister(ref->dpll, pin);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
 			mutex_unlock(&dpll_device_xa_lock);
 		}
 	}
 	refcount_dec(&pin->refcount);
-	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
 unlock:
 	mutex_unlock(&dpll_pin_xa_lock);
 	return ret;
@@ -779,20 +843,23 @@ 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)
+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_device_xa_lock);
 	mutex_lock(&dpll_pin_xa_lock);
-	dpll_xa_ref_pin_del(&pin->parent_refs, parent);
+	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);
+		__dpll_pin_unregister(ref->dpll, pin, ops, priv);
 		dpll_pin_parent_notify(ref->dpll, pin, parent,
 				       DPLL_A_PIN_IDX);
 	}
@@ -859,6 +926,17 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *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
@@ -869,13 +947,14 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
 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 = dpll_xa_ref_pin_find((struct xarray *)&dpll->pin_refs, pin);
 	if (!ref)
 		return NULL;
-
-	return ref->priv;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_priv);
 
@@ -889,16 +968,25 @@ EXPORT_SYMBOL_GPL(dpll_pin_on_dpll_priv);
 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 = dpll_xa_ref_pin_find((struct xarray *)&pin->parent_refs, parent);
 	if (!ref)
 		return NULL;
-
-	return ref->priv;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
 }
 EXPORT_SYMBOL_GPL(dpll_pin_on_pin_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;
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 636dd4c6710e..2ab2d9e0a3cd 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -73,27 +73,30 @@ struct dpll_pin {
 	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
- * @ops:		ops for a dpll pin
- * @priv:		pointer to private information of owner
  **/
 struct dpll_pin_ref {
 	union {
 		struct dpll_device *dpll;
 		struct dpll_pin *pin;
 	};
-	struct dpll_pin_ops *ops;
-	void *priv;
-	refcount_t refcount;
+	struct list_head registration_list;
 };
 
 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 *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx);
 struct dpll_pin_ref *
 dpll_xa_ref_pin_find(struct xarray *xa_refs, const struct dpll_pin *pin);
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 41e2f8a90aeb..125dc3c7e643 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -103,11 +103,12 @@ 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);
 	u32 prio;
 
-	if (!ref->ops->prio_get)
+	if (!ops->prio_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->prio_get(pin, ref->dpll, &prio, extack))
+	if (ops->prio_get(pin, ref->dpll, &prio, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
 		return -EMSGSIZE;
@@ -120,11 +121,12 @@ 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);
 	enum dpll_pin_state state;
 
-	if (!ref->ops->state_on_dpll_get)
+	if (!ops->state_on_dpll_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_dpll_get(pin, ref->dpll, &state, extack))
+	if (ops->state_on_dpll_get(pin, ref->dpll, &state, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
 		return -EMSGSIZE;
@@ -137,11 +139,12 @@ 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);
 	enum dpll_pin_direction direction;
 
-	if (!ref->ops->direction_get)
+	if (!ops->direction_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->direction_get(pin, ref->dpll, &direction, extack))
+	if (ops->direction_get(pin, ref->dpll, &direction, extack))
 		return -EFAULT;
 	if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
 		return -EMSGSIZE;
@@ -154,12 +157,13 @@ 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_any_freq)
 {
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
 	enum dpll_pin_freq_supp fs;
 	u32 freq;
 
-	if (!ref->ops->frequency_get)
+	if (!ops->frequency_get)
 		return -EOPNOTSUPP;
-	if (ref->ops->frequency_get(pin, ref->dpll, &freq, extack))
+	if (ops->frequency_get(pin, ref->dpll, &freq, extack))
 		return -EFAULT;
 	if (nla_put_u32(msg, DPLL_A_PIN_FREQUENCY, freq))
 		return -EMSGSIZE;
@@ -196,10 +200,12 @@ dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
 	int ret;
 
 	xa_for_each(&pin->parent_refs, index, ref_parent) {
-		if (WARN_ON(!ref_parent->ops->state_on_pin_get))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref_parent);
+
+		if (WARN_ON(!ops->state_on_pin_get))
 			return -EFAULT;
-		ret = ref_parent->ops->state_on_pin_get(pin, ref_parent->pin,
-							&state, extack);
+		ret = ops->state_on_pin_get(pin, ref_parent->pin,
+					    &state, extack);
 		if (ret)
 			return -EFAULT;
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
@@ -235,10 +241,11 @@ dpll_msg_add_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
 	int ret;
 
 	xa_for_each(&pin->parent_refs, index, ref) {
-		if (WARN_ON(!ref->ops->state_on_pin_get))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		if (WARN_ON(!ops->state_on_pin_get))
 			return -EFAULT;
-		ret = ref->ops->state_on_pin_get(pin, ref->pin, &state,
-						 extack);
+		ret = ops->state_on_pin_get(pin, ref->pin, &state, extack);
 		if (ret)
 			return -EFAULT;
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
@@ -452,7 +459,9 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EINVAL;
 
 	xa_for_each(&pin->dpll_refs, i, ref) {
-		ret = ref->ops->frequency_set(pin, ref->dpll, freq, extack);
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		ret = ops->frequency_set(pin, ref->dpll, freq, extack);
 		if (ret)
 			return -EFAULT;
 		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_FREQUENCY);
@@ -466,6 +475,7 @@ 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 *ref;
 	struct dpll_pin *parent;
 
@@ -477,9 +487,10 @@ dpll_pin_on_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
 	if (!ref)
 		return -EINVAL;
-	if (!ref->ops || !ref->ops->state_on_pin_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_pin_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_pin_set(pin, parent, state, extack))
+	if (ops->state_on_pin_set(pin, parent, state, extack))
 		return -EFAULT;
 	dpll_pin_parent_notify(dpll, pin, parent, DPLL_A_PIN_STATE);
 
@@ -491,6 +502,7 @@ 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))
@@ -498,9 +510,10 @@ dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
 	if (!ref)
 		return -EFAULT;
-	if (!ref->ops || !ref->ops->state_on_dpll_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_dpll_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->state_on_dpll_set(pin, ref->dpll, state, extack))
+	if (ops->state_on_dpll_set(pin, ref->dpll, state, extack))
 		return -EINVAL;
 	dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_STATE);
 
@@ -511,6 +524,7 @@ 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);
 
@@ -519,9 +533,10 @@ dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
 	ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
 	if (!ref)
 		return -EFAULT;
-	if (!ref->ops || !ref->ops->prio_set)
+	ops = dpll_pin_ops(ref);
+	if (!ops->prio_set)
 		return -EOPNOTSUPP;
-	if (ref->ops->prio_set(pin, dpll, prio, extack))
+	if (ops->prio_set(pin, dpll, prio, extack))
 		return -EINVAL;
 	dpll_pin_notify(dpll, pin, DPLL_A_PIN_PRIO);
 
@@ -540,7 +555,9 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EOPNOTSUPP;
 
 	xa_for_each(&pin->dpll_refs, i, ref) {
-		if (ref->ops->direction_set(pin, ref->dpll, direction, extack))
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+
+		if (ops->direction_set(pin, ref->dpll, direction, extack))
 			return -EFAULT;
 		dpll_pin_notify(ref->dpll, pin, DPLL_A_PIN_DIRECTION);
 	}
@@ -920,13 +937,15 @@ dpll_event_device_change(struct sk_buff *msg, struct dpll_device *dpll,
 		break;
 	case DPLL_A_PIN_STATE:
 		if (parent) {
+			const struct dpll_pin_ops *ops;
+
 			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
 			if (!ref)
 				return -EFAULT;
-			if (!ref->ops || !ref->ops->state_on_pin_get)
+			ops = dpll_pin_ops(ref);
+			if (!ops->state_on_pin_get)
 				return -EOPNOTSUPP;
-			ret = ref->ops->state_on_pin_get(pin, parent, &state,
-							 NULL);
+			ret = ops->state_on_pin_get(pin, parent, &state, NULL);
 			if (ret)
 				return ret;
 			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 532ad7314f49..d39292ad102f 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1194,7 +1194,8 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
 		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
 		if (!parent)
 			continue;
-		dpll_pin_on_pin_unregister(parent, rclk->pin);
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, pf);
 	}
 	dpll_pin_put(rclk->pin);
 	rclk->pin = NULL;
@@ -1202,6 +1203,7 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
 
 /**
  * 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
@@ -1215,7 +1217,7 @@ ice_dpll_release_rclk_pin(struct ice_pf *pf)
  * * positive - number of errors encounterd on pin's deregistration.
  */
 static int
-ice_dpll_release_pins(struct dpll_device *dpll_eec,
+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, bool cgu)
 {
@@ -1226,12 +1228,16 @@ ice_dpll_release_pins(struct dpll_device *dpll_eec,
 
 		if (p && !IS_ERR_OR_NULL(p->pin)) {
 			if (cgu && dpll_eec) {
-				ret = dpll_pin_unregister(dpll_eec, p->pin);
+				ret = dpll_pin_unregister(dpll_eec, p->pin,
+							  &ice_dpll_source_ops,
+							  pf);
 				if (ret)
 					err++;
 			}
 			if (cgu && dpll_pps) {
-				ret = dpll_pin_unregister(dpll_pps, p->pin);
+				ret = dpll_pin_unregister(dpll_pps, p->pin,
+							  &ice_dpll_source_ops,
+							  pf);
 				if (ret)
 					err++;
 			}
@@ -1532,7 +1538,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 
 	mutex_lock(&pf->dplls.lock);
 	ice_dpll_release_rclk_pin(pf);
-	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+	ret = ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->inputs,
 				    d->num_inputs, cgu);
 	mutex_unlock(&pf->dplls.lock);
 	if (ret)
@@ -1540,7 +1546,7 @@ static void ice_dpll_release_all(struct ice_pf *pf, bool cgu)
 			 "source pins release dplls err=%d\n", ret);
 	if (cgu) {
 		mutex_lock(&pf->dplls.lock);
-		ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+		ret = ice_dpll_release_pins(pf, de->dpll, dp->dpll, d->outputs,
 					    d->num_outputs, cgu);
 		mutex_unlock(&pf->dplls.lock);
 		if (ret)
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 09863d66a44c..be5717e1da99 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -211,6 +211,8 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * 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.
  *
@@ -219,7 +221,8 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
  * * -ENXIO - given pin was not registered with this dpll device,
  * * -EINVAL - pin pointer is not valid.
  */
-int dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin);
+int 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
@@ -268,7 +271,8 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *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);
+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
-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 5/7] dpll: export dpll_pin_notify()
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Export dpll_pin_notify() as it is needed to be called from drivers.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_netlink.c | 1 +
 drivers/dpll/dpll_netlink.h | 3 ---
 include/linux/dpll.h        | 4 ++++
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 125dc3c7e643..cd77881ee1ec 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -1056,6 +1056,7 @@ int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
 {
 	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)
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
index 072efa10f0e6..952e0335595e 100644
--- a/drivers/dpll/dpll_netlink.h
+++ b/drivers/dpll/dpll_netlink.h
@@ -20,9 +20,6 @@ int dpll_notify_device_create(struct dpll_device *dpll);
  */
 int dpll_notify_device_delete(struct dpll_device *dpll);
 
-int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
-		    enum dplla attr);
-
 int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
 			   struct dpll_pin *parent, enum dplla attr);
 
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index be5717e1da99..562b9b7bd001 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -287,5 +287,9 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
  */
 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
-- 
2.39.0


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

* [patch dpll-rfc 5/7] dpll: export dpll_pin_notify()
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

From: Jiri Pirko <jiri@nvidia.com>

Export dpll_pin_notify() as it is needed to be called from drivers.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
---
 drivers/dpll/dpll_netlink.c | 1 +
 drivers/dpll/dpll_netlink.h | 3 ---
 include/linux/dpll.h        | 4 ++++
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 125dc3c7e643..cd77881ee1ec 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -1056,6 +1056,7 @@ int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
 {
 	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)
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
index 072efa10f0e6..952e0335595e 100644
--- a/drivers/dpll/dpll_netlink.h
+++ b/drivers/dpll/dpll_netlink.h
@@ -20,9 +20,6 @@ int dpll_notify_device_create(struct dpll_device *dpll);
  */
 int dpll_notify_device_delete(struct dpll_device *dpll);
 
-int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
-		    enum dplla attr);
-
 int dpll_pin_parent_notify(struct dpll_device *dpll, struct dpll_pin *pin,
 			   struct dpll_pin *parent, enum dplla attr);
 
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index be5717e1da99..562b9b7bd001 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -287,5 +287,9 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
  */
 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
-- 
2.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 6/7] netdev: expose DPLL pin handle for netdevice
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	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 is 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  | 28 ++++++++++++++++++++++----
 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, 111 insertions(+), 4 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index cd77881ee1ec..ac5cbf5a2e19 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -302,6 +302,24 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
 	return ret;
 }
 
+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->dev_driver_id))
+		return -EMSGSIZE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
+
 static int
 dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 			 struct dpll_device *dpll,
@@ -310,8 +328,9 @@ dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 	struct dpll_pin_ref *ref;
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
@@ -352,8 +371,9 @@ __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 	struct dpll_pin_ref *ref;
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 562b9b7bd001..f9081e5fc522 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -92,6 +92,26 @@ enum dpll_pin_freq_supp {
 	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
 };
 
+#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 7621c512765f..79a90b52ee77 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
@@ -2403,6 +2404,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)
 
@@ -3939,6 +3944,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 57ceb788250f..280573f723da 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 c7853192563d..cf9f61cf33e2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8949,6 +8949,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 b7b1661d0d56..e29aed39625d 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.39.0


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

* [patch dpll-rfc 6/7] netdev: expose DPLL pin handle for netdevice
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	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 is 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  | 28 ++++++++++++++++++++++----
 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, 111 insertions(+), 4 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index cd77881ee1ec..ac5cbf5a2e19 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -302,6 +302,24 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
 	return ret;
 }
 
+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->dev_driver_id))
+		return -EMSGSIZE;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
+
 static int
 dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 			 struct dpll_device *dpll,
@@ -310,8 +328,9 @@ dpll_cmd_pin_on_dpll_get(struct sk_buff *msg, struct dpll_pin *pin,
 	struct dpll_pin_ref *ref;
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
@@ -352,8 +371,9 @@ __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
 	struct dpll_pin_ref *ref;
 	int ret;
 
-	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
-		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
 	if (nla_put_string(msg, DPLL_A_PIN_DESCRIPTION, pin->prop.description))
 		return -EMSGSIZE;
 	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, pin->prop.type))
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 562b9b7bd001..f9081e5fc522 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -92,6 +92,26 @@ enum dpll_pin_freq_supp {
 	DPLL_PIN_FREQ_SUPP_MAX = (__DPLL_PIN_FREQ_SUPP_MAX - 1)
 };
 
+#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 7621c512765f..79a90b52ee77 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
@@ -2403,6 +2404,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)
 
@@ -3939,6 +3944,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 57ceb788250f..280573f723da 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 c7853192563d..cf9f61cf33e2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8949,6 +8949,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 b7b1661d0d56..e29aed39625d 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.39.0


_______________________________________________
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] 202+ messages in thread

* [patch dpll-rfc 7/7] mlx5: Implement SyncE support using DPLL infrastructure
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-26 17:00     ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	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    | 429 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 517 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 6c2f1d4a58ab..c2c864aba1f5 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 445fe30c3d0b..7520e5987cb2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -227,6 +227,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,
@@ -236,6 +249,8 @@ enum {
 	MLX5_INTERFACE_PROTOCOL_MPIB,
 
 	MLX5_INTERFACE_PROTOCOL_VNET,
+
+	MLX5_INTERFACE_PROTOCOL_DPLL,
 };
 
 static const struct mlx5_adev_device {
@@ -258,6 +273,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..afb092fcbcae
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,429 @@
+// 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,
+					    enum dpll_lock_status *status,
+					    struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_priv(dpll);
+	enum mlx5_msees_oper_status oper_status;
+	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,
+				     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,
+					    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,
+				       const struct dpll_device *dpll,
+				       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,
+				       const struct dpll_device *dpll,
+				       enum dpll_pin_state *state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_pin_on_dpll_priv(dpll, pin);
+	enum mlx5_msees_admin_status admin_status;
+	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,
+				       const struct dpll_device *dpll,
+				       enum dpll_pin_state state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_pin_on_dpll_priv(dpll, pin);
+
+	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 = {
+	.description = "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, DPLL_TYPE_EEC, THIS_MODULE);
+	if (IS_ERR(mdpll->dpll)) {
+		err = PTR_ERR(mdpll->dpll);
+		goto err_free_mdpll;
+	}
+
+	err = dpll_device_register(mdpll->dpll, &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 7a898113b6b7..3cfa30b86878 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 e47d6c58da35..6ce11b43bf39 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10143,7 +10143,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];
 
@@ -12505,4 +12507,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.39.0


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

* [patch dpll-rfc 7/7] mlx5: Implement SyncE support using DPLL infrastructure
@ 2023-03-26 17:00     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-03-26 17:00 UTC (permalink / raw)
  To: netdev, arkadiusz.kubalewski, vadim.fedorenko, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	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    | 429 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 517 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 6c2f1d4a58ab..c2c864aba1f5 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 445fe30c3d0b..7520e5987cb2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -227,6 +227,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,
@@ -236,6 +249,8 @@ enum {
 	MLX5_INTERFACE_PROTOCOL_MPIB,
 
 	MLX5_INTERFACE_PROTOCOL_VNET,
+
+	MLX5_INTERFACE_PROTOCOL_DPLL,
 };
 
 static const struct mlx5_adev_device {
@@ -258,6 +273,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..afb092fcbcae
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,429 @@
+// 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,
+					    enum dpll_lock_status *status,
+					    struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_priv(dpll);
+	enum mlx5_msees_oper_status oper_status;
+	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,
+				     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,
+					    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,
+				       const struct dpll_device *dpll,
+				       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,
+				       const struct dpll_device *dpll,
+				       enum dpll_pin_state *state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_pin_on_dpll_priv(dpll, pin);
+	enum mlx5_msees_admin_status admin_status;
+	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,
+				       const struct dpll_device *dpll,
+				       enum dpll_pin_state state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = dpll_pin_on_dpll_priv(dpll, pin);
+
+	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 = {
+	.description = "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, DPLL_TYPE_EEC, THIS_MODULE);
+	if (IS_ERR(mdpll->dpll)) {
+		err = PTR_ERR(mdpll->dpll);
+		goto err_free_mdpll;
+	}
+
+	err = dpll_device_register(mdpll->dpll, &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 7a898113b6b7..3cfa30b86878 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 e47d6c58da35..6ce11b43bf39 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10143,7 +10143,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];
 
@@ -12505,4 +12507,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.39.0


_______________________________________________
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] 202+ messages in thread

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

On 15/03/2023 09:22, Jiri Pirko wrote:
> Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>> From: Jiri Pirko <jiri@resnulli.us>
>>> Sent: Tuesday, March 14, 2023 10:22 AM
>>>
>>> Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>> On 13.03.2023 16:21, Jiri Pirko wrote:
>>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>>>
>>> [...]
>>>
>>>
>>>>>> diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>>>>> new file mode 100644
>>>>>> index 000000000000..d3926f2a733d
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/dpll/Makefile
>>>>>> @@ -0,0 +1,10 @@
>>>>>> +# SPDX-License-Identifier: GPL-2.0
>>>>>> +#
>>>>>> +# Makefile for DPLL drivers.
>>>>>> +#
>>>>>> +
>>>>>> +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>>>
>>>>> What's "sys" and why is it here?
>>>>
>>>> It's an object file for the subsystem. Could be useful if we will have
>>>> drivers
>>>> for DPLL-only devices.
>>>
>>> Yeah, but why "sys"? I don't get what "sys" means here.
>>> Can't this be just "dpll.o"?
>>>
>>>
>>>>
>>>>>> +dpll_sys-y                  += dpll_core.o
>>>>>> +dpll_sys-y                  += dpll_netlink.o
>>>>>> +dpll_sys-y                  += dpll_nl.o
>>>>>> +
>>>
>>> [...]
>>>
>>>
>>>>>> +struct dpll_device *
>>>>>> +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>>>>> *module)
>>>>>> +{
>>>>>> +	struct dpll_device *dpll, *ret = NULL;
>>>>>> +	unsigned long index;
>>>>>> +
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>>>> +		if (dpll->clock_id == clock_id &&
>>>>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>>>>
>>>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>>>> identification, isn't that enough for you.
>>>>
>>>> dev_driver_id is needed to provide several DPLLs from one device. In ice
>>>> driver
>>>> implementation there are 2 different DPLLs - to recover from PPS input and
>>>> to
>>>> recover from Sync-E. I believe there is only one clock, that's why clock id
>>>> is the same for both of them. But Arkadiusz can tell more about it.
>>>
>>> Okay, I see. Clock_id is the same. Could we have index for pin, could
>>> this be index too:
>>>
>>> dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>>> dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>>> 	     const struct dpll_pin_properties *prop);
>>>
>>> This way it is consistent, driver provides custom index for both dpll
>>> device and dpll pin.
>>>
>>> Makes sense?
>>>
>>
>> IMHO, Yes this better shows the intentions.
> 
> Ok.
> 
> 
>>
>>>
>>>>>
>>>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>>>
>>>> Simply id doesn't tell anything either. dpll_dev_id?
>>>
>>> Yeah, see above.
>>>
>>>
>>>>
>>>>>> +		    dpll->module == module) {
>>>>>> +			ret = dpll;
>>>>>> +			refcount_inc(&ret->refcount);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	}
>>>>>> +	if (!ret)
>>>>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>>>> +	mutex_unlock(&dpll_device_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;
>>>>>
>>>>> Remove this check. The driver should not call this with NULL.
>>>>
>>>> Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>>> i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>>
>>> IDK, maybe for historical reasons. My point is, id driver is callin
>>> this with NULL, there is something odd in the driver flow. Lets not
>>> allow that for new code.
>>>
>>>
>>>>
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>>>
>>>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>>>
>>>> Good point!
>>>>
>>>>>> +		xa_destroy(&dpll->pin_refs);
>>>>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>>>>> +		kfree(dpll);
>>>>>> +	}
>>>>>> +	mutex_unlock(&dpll_device_xa_lock);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>>>> +
>>>>>> +/**
>>>>>> + * 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,
>>>>>> +			 struct dpll_device_ops *ops, void *priv,
>>>>>> +			 struct device *owner)
>>>>>> +{
>>>>>> +	if (WARN_ON(!ops || !owner))
>>>>>> +		return -EINVAL;
>>>>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>>>> +		return -EINVAL;
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>>>> +		mutex_unlock(&dpll_device_xa_lock);
>>>>>> +		return -EEXIST;
>>>>>> +	}
>>>>>> +	dpll->dev.bus = owner->bus;
>>>>>> +	dpll->parent = owner;
>>>>>> +	dpll->type = type;
>>>>>> +	dpll->ops = ops;
>>>>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>>>> +		     dpll->dev_driver_id);
>>>>>
>>>>> This is really odd. As a result, the user would see something like:
>>>>> pci/0000:01:00.0_1
>>>>> pci/0000:01:00.0_2
>>>>>
>>>>> I have to say it is confusing. In devlink, is bus/name and the user
>>>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>>>> there. Also, "_" might have some meaning on some bus. Should not
>>>>> concatename dev_name() with anything.
>>>>>
>>>>> Thinking about this some more, the module/clock_id tuple should be
>>>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>>>> as the user handle, can't it?
>>>>> Example:
>>>>> ice/c92d02a7129f4747
>>>>> mlx5/90265d8bf6e6df56
>>>>>
>>>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>>>> enough), you can put it here as well:
>>>>> ice/c92d02a7129f4747/1
>>>>> ice/c92d02a7129f4747/2
>>>>>
>>>>
>>>> Looks good, will change it
>>>
>>> Great.
>>>
>>>
>>>>
>>>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>>>> share instance of DPLL equally, there is no "one clock master". >
>>>
>>> [...]
>>>
>>>
>>>>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>>>> +	if (!pin->prop.description) {
>>>>>> +		ret = -ENOMEM;
>>>>>> +		goto release;
>>>>>> +	}
>>>>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>>>> +		ret = -EINVAL;
>>>>>> +		goto release;
>>>>>> +	}
>>>>>> +	pin->prop.type = prop->type;
>>>>>> +	pin->prop.capabilities = prop->capabilities;
>>>>>> +	pin->prop.freq_supported = prop->freq_supported;
>>>>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>>>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>>>>
>>>>> Make sure that the driver maintains prop (static const) and just save
>>>>> the pointer. Prop does not need to be something driver needs to change.
>>>>>
>>>>
>>>> What's the difference? For ptp_ocp, we have the same configuration for all
>>>> ext pins and the allocator only changes the name of the pin. Properties of
>>>> the DPLL pins are stored within the pin object, not the driver, in this
>>> case.
>>>> Not sure if the pointer way is much better...
>>>
>>> For things like this it is common to have static const array in the
>>> driver, like:
>>>
>>> static const struct dpll_pin_properties dpll_pin_props[] = {
>>> 	{
>>> 		.description = "SMA0",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA1",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA2",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA3",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> };
>>>
>>> Here you have very nice list of pins, the reader knows right away what
>>> is happening.
>>>
>>> Thinking about "description" name, I think would be more appropriate to
>>> name this "label" as it represents user-facing label on the connector,
>>> isn't it? Does not describe anything.
>>>
>>
>> "label" seems good.
> 
> Ok.
> 
> 
>>
>>>
>>>>
>>>>>
>>>
>>> [...]
>>>
>>>
>>>>
>>>>>
>>>>>> +	     const struct dpll_pin_properties *prop)
>>>>>> +{
>>>>>> +	struct dpll_pin *pos, *ret = NULL;
>>>>>> +	unsigned long index;
>>>>>> +
>>>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>>>> +		if (pos->clock_id == clock_id &&
>>>>>> +		    pos->dev_driver_id == device_drv_id &&
>>>>>> +		    pos->module == module) {
>>>>>
>>>>> Compare prop as well.
>>>>>
>>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>>> should. And therefore it could be easily put inside.
>>>>>
>>>>
>>>> I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>> And now I think that *prop is not needed here at all. Arkadiusz, any
>>>> thoughts?
>>>
>>> IDK, no strong opinion on this. I just thought it may help to identify
>>> the pin and avoid potential driver bugs. (Like registering 2 pins with
>>> the same properties).
>>>
>>
>> It would make most sense if pin_index would be a part of *prop.
> 
> Hmm. I see one example where it would not be suitable:
> NIC with N physical ports. You, as a driver, register one pin per
> physical port. You have one static const prop and pass the pointer to if
> for every registration call of N pins, each time with different
> pin_index.
> 
> So from what I see, the prop describes a flavour of pin in the driver.
> In the exaple above, it is still the same SyncE pin, with the same ops.
> Only multiple instances of that distinguished by pin_index and priv.
> 
> Makes sense?
> 
> 
>>
>>> [...]
>>>
>>>
>>>>>> +/**
>>>>>> + * 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,
>>>>>> +		  struct dpll_pin_ops *ops, void *priv,
>>>>>> +		  struct device *rclk_device)
>>>>>
>>>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>>>
>>>>>
>>>>>> +{
>>>>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>>>>> NULL;
>>>>>
>>>>> If you need to store something here, store the pointer to the device
>>>>> directly. But this rclk_device seems odd to me.
>>>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>>>> is incomplete. What should it server for?
>>>>>
>>>>
>>>> Well, these questions go to Arkadiusz...
>>>
>>
>>
>> [ copy paste my answer from previous response ]
> 
> Does not help, see below.
> 
> 
>> If pin is able to recover signal from some device this shall convey that
>> device struct pointer.
> 
> But one device (struct device instance) could easily have multiple
> netdev instances attached and therefore mutiple sources of recovered
> signal.
> 
> mlxsw driver is one of the examples of this 1:N mapping between device
> and netdev.
> 
> 
>> Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>> attribute.
>> Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>> part. But isn't it better to have the name ready to use there?
>>
>> It might be incomplete only if one device would have some kind of access to
>> a different bus? I don't think it is valid use case.
> 
> Very easily, auxiliary_bus. That is actually the case already for mlx5.
> 
> 
>>
>> Basically the driver will refer only to the devices handled by that driver,
>> which means if dpll is on some bus, also all the pins are there, didn't notice
>> the need to have bus here as well.
> 
> What is the motivation exactly? Is it only SyncE? In case it is, the
> link to the pin should be added from the other side, carried over
> RTNetlink msg of a netdev associated with pin. I will add a patch for
> this.
> 
> Do you need to expose any other recovered clock device source?
> 
> 
>>
>>> Okay.
>>>
>>> [...]
>>>
>>>
>>>>>> + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>>>>> + * @dpll: dpll device pointer
>>>>>> + * @idx: index of pin
>>>>>> + *
>>>>>> + * Find a reference to a pin registered with given dpll and return
>>>>>> its pointer.
>>>>>> + *
>>>>>> + * Return:
>>>>>> + * * valid pointer if pin was found
>>>>>> + * * NULL if not found
>>>>>> + */
>>>>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>>>> +{
>>>>>> +	struct dpll_pin_ref *pos;
>>>>>> +	unsigned long i;
>>>>>> +
>>>>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>>>
>>>>> How exactly pos->pin could be NULL?
>>>>>
>>>>> Also, you are degrading the xarray to a mere list here with lookup like
>>>>> this. Why can't you use the pin index coming from driver and
>>>>> insert/lookup based on this index?
>>>>>
>>>> Good point. We just have to be sure, that drivers provide 0-based indexes for
>>>> their pins. I'll re-think it.
>>>
>>> No, driver can provide indexing which is completely up to his decision.
>>> You should use xa_insert() to insert the entry at specific index. See
>>> devl_port_register for inspiration where it is done exactly like this.
>>>
>>> And this should be done in exactly the same way for both pin and device.
>>>
>>
>> Yes, I agree seems doable and better then it is now.
>>
>> [...]
>>
>>>>>> +static int
>>>>>> +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>>>>> *dpll)
>>>>>> +{
>>>>>> +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>>>
>>>>> Why exactly do we need this dua--handle scheme? Why do you need
>>>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>>>> It's just confusing.
>>>>>
>>>> To be able to work with DPLL per integer after iterator on the list deducts
>>>> which DPLL device is needed. It can reduce the amount of memory copies and
>>>> simplify comparisons. Not sure why it's confusing.
>>>
>>> Wait, I don't get it. Could you please explain a bit more?
>>>
>>> My point is, there should be not such ID exposed over netlink
>>> You don't need to expose it to userspace. The user has well defined
>>> handle as you agreed with above. For example:
>>>
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>> This is shown in dpll device GET/DUMP outputs.
>>> Also user passes it during SET operation:
>>> $ dplltool set ice/c92d02a7129f4747/1 mode auto
>>>
>>> Isn't that enough stable and nice?
>>>
>>
>> I agree with Vadim, this is rather to be used by a daemon tools, which
>> would get the index once, then could use it as long as device is there.
> 
> So basically you say, you can have 2 approaches in app:
> 1)
> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> 2):
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> 
> What is exactly benefit of the first one? Why to have 2 handles? Devlink
> is a nice example of 2) approach, no problem there.
> 
> Perhaps I'm missing something, but looks like you want the id for no
> good reason and this dual-handle scheme just makes things more
> complicated with 0 added value.

I would like to avoid any extra memory copies or memory checks when it's 
possible to compare single u32/u64 index value. I might be invisible on 
a single host setup, but running monitoring at scale which will parse 
and compare string on every get/event can burn a bit of compute capacity.

> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>>>> +			 struct netlink_ext_ack *extack)
>>>>>> +{
>>>>>> +	struct dpll_pin_ref *ref = NULL;
>>>>>
>>>>> Why this needs to be initialized?
>>>>>
>>>> No need, fixed.
>>>>
>>>>
>>>>>
>>>>>> +	enum dpll_pin_state state;
>>>>>> +	struct nlattr *nest;
>>>>>> +	unsigned long index;
>>>>>> +	int ret;
>>>>>> +
>>>>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>>>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>>>> +			return -EFAULT;
>>>>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>>>> +				ref->pin->dev_driver_id)) {
>>>>>> +			ret = -EMSGSIZE;
>>>>>> +			goto nest_cancel;
>>>>>> +		}
>>>>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>>>> +			ret = -EMSGSIZE;
>>>>>> +			goto nest_cancel;
>>>>>> +		}
>>>>>> +		nla_nest_end(msg, nest);
>>>>>> +	}
>>>>>
>>>>> How is this function different to dpll_msg_add_pin_parents()?
>>>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>>>> hard to follow for me :/
>>>>>
>>>>> Did you get lost here as well? If yes, this needs some serious think
>>>>> through :)
>>>>>
>>>>
>>>> Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>>> relationship between these things?
>>>
>>
>> [ copy paste my answer from previous response ]
>> No, it is just leftover I didn't catch, we can leave one function and use it in
>> both cases. Sorry about that, great catch!
> 
> Hmm, ok. I'm still lost a bit with all the referencing and cross
> referencing, I wonder if it could be made a bit simpler and/or easier to
> read and follow.
> 
> 
>>
>> [...]
>>
>>>>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>>>
>>>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>>>> code.
>>>>>
>>>>
>>>> I believe it's INDEX which is provided by the driver. I'll think about
>>>> renaming,
>>>> but suggestions are welcome.
>>>
>>> Let's use "index" and "INDEX" internalla and in Netlink attr names as
>>> well then.
>>>
>>
>> For me makes sense to have a common name instead of origin-based one.
> 
> What do you mean by this?
> 
> 
>>
>>> [...]
>>>
>>>
>>>>
>>>>>
>>>>>> +	int rem, ret = -EINVAL;
>>>>>> +	struct nlattr *a;
>>>>>> +
>>>>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>>>> +			  genlmsg_len(info->genlhdr), rem) {
>>>>>
>>>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>>>
>>>>
>>>> I had some unknown crashes when I was using such access. I might have lost
>>>> some checks, will try it again.
>>>
>>> Odd, yet definitelly debuggable though :)
>>>
>>> [...]
>>>
>>>
>>>>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>>>> +{
>>>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>>>
>>>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>>>> taking order is opposite.
>>>>>
>>>>
>>>> Now I see an ABBA deadlock here, as well as in function before. Not sure
>>>> how to
>>>> solve it here. Any thoughts?
>>>
>>> Well, here you can just call dpll_pre_dumpit() before
>>> mutex_lock(&dpll_pin_xa_lock)
>>> to take the locks in the same order.
>>>
>>
>> This double lock doesn't really improve anything.
>> Any objections on having single mutex/lock for access the dpll subsystem?
> 
> Yeah, we had it in devlink and then we spent a lot of a time to get rid
> of it. Now we have per-instance locking. Here, it would be
> per-dpll-device locking.
> 
> No strong opinion, perhaps on case of dpll one master lock is enough.
> Perhaps Jakub would have some opinion on this.
> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	return dpll_pre_dumpit(cb);
>>>>>> +}
>>>>>> +
>>>>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>>>> +{
>>>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>>>> +
>>>>>> +	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-
>>>>>> dev_driver_id))
>>>>>> +		return -EMSGSIZE;
>>>>>
>>>>> I don't really understand why you are trying figure something new and
>>>>> interesting with the change notifications. This object mix and random
>>>>> attrs fillup is something very wrong and makes userspace completely
>>>>> fuzzy about what it is getting. And yet it is so simple:
>>>>> You have 2 objects, dpll and pin, please just have:
>>>>> dpll_notify()
>>>>> dpll_pin_notify()
>>>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>>>> No need for any smartness. Have this dumb and simple.
>>>>>
>>>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>>>
>>>> But with full object-snapshot user space app will lose the information about
>>>> what exactly has changed. The reason to have this event is to provide the
>>>> attributes which have changed. Otherwise, the app should have full snapshot
>>>> and
>>>> compare all attributes to figure out changes and that's might not be great
>>>> idea.
>>>
>>> Wait, are you saying that the app is stateless? Could you provide
>>> example use cases?
>>>
>> >From what I see, the app managing dpll knows the state of the device and
>>> pins, it monitors for the changes and saves new state with appropriate
>>> reaction (might be some action or maybe just log entry).
>>>
>>
>> It depends on the use case, right? App developer having those information knows
>> what has changed, thus can react in a way it thinks is most suitable.
>> IMHO from user perspective it is good to have a notification which actually
>> shows it's reason, so proper flow could be assigned to handle the reaction.
> 
> Again, could you provide me specific example in which this is needed?
> I may be missing something, but I don't see how it can bring
> and benefit. It just makes the live of the app harder because it has to
> treat the get and notify messages differently.
> 
> It is quite common for app to:
> init:
> 1) get object state
> 2) store it
> 3) apply configuration
> runtime:
> 1) listen to object state change
> 2) store it
> 3) apply configuration
> 
> Same code for both.

Well, I'm thinking about simple monitoring app which will wait for the 
events and create an alert if the changes coming from the event differ 
from the "allowed configs". In this case no real reason to store whole 
object state, but the changes in events are very useful.

> 
> 
> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	switch (attr) {
>>>>>> +	case DPLL_A_MODE:
>>>>>> +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>>>>> +		break;
>>>>>> +	case DPLL_A_SOURCE_PIN_IDX:
>>>>>> +		ret = dpll_msg_add_source_pin_idx(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:
>>>>>> +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>>>>> +		break;
>>>>>> +	case DPLL_A_PIN_PRIO:
>>>>>> +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>>>> +		if (!ref)
>>>>>> +			return -EFAULT;
>>>>>> +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>>>>> +		break;
>>>>>> +	case DPLL_A_PIN_STATE:
>>>>>> +		if (parent) {
>>>>>> +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>>>>> +			if (!ref)
>>>>>> +				return -EFAULT;
>>>>>> +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>>>>> +				return -EOPNOTSUPP;
>>>>>> +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>>>>> +							 NULL);
>>>>>> +			if (ret)
>>>>>> +				return ret;
>>>>>> +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>>>>> +					parent->dev_driver_id))
>>>>>> +				return -EMSGSIZE;
>>>>>> +		} else {
>>>>>> +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>>>> +			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);
>>>>>
>>>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>>>> I have to be missing something.
>>>>
>>>> Changes might come from other places, but will affect the DPLL device and we
>>>> have to notify users in this case.
>>>
>>> I'm not sure I follow. There are 2 scenarios for change:
>>> 1) user originated - user issues set of something
>>> 2) driver originated - something changes in HW, driver propagates that
>>>
>>> With what I suggest, both scenarios work of course. My point is, user
>>> knows very well the objects: device and pin, he knows the format or
>>> messages that are related to GET/DUMP/SET operations of both. The
>>> notification should have the same format, that is all I say.
>>>
>>> Btw, you can see devlink code for example how the notifications like
>>> this are implemented and work.
>>>
>>
>> Devlink packs all the info into a netlink message and notifies with it, isn't
>> it that it has all the info "buffered" in its structures?
> 
> Not really. Ops are there as well.
> 
> 
>> A dpll subsystem keeps only some of the info in its internal structures, but
>> for most of it it has to question the driver. It would do it multiple times
>> i.e. if there would be a change on a pin connected to multiple dpll devices.
>>
>> With current approach the notifications are pretty light to the subsystem as
>> well as for the registered clients, and as you suggested with having info of
>> what actually changed the userspace could implement a stateless daemon right
>> away.
> 
> Okay, examples? I really can't see how this stateless deamon could be
> implemented, but perhaps I lack better imagination :/
> 
> 
>>
>> 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] 202+ messages in thread

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

On 15/03/2023 09:22, Jiri Pirko wrote:
> Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>>> From: Jiri Pirko <jiri@resnulli.us>
>>> Sent: Tuesday, March 14, 2023 10:22 AM
>>>
>>> Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>>>> On 13.03.2023 16:21, Jiri Pirko wrote:
>>>>> Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:
>>>
>>> [...]
>>>
>>>
>>>>>> diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>>>>> new file mode 100644
>>>>>> index 000000000000..d3926f2a733d
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/dpll/Makefile
>>>>>> @@ -0,0 +1,10 @@
>>>>>> +# SPDX-License-Identifier: GPL-2.0
>>>>>> +#
>>>>>> +# Makefile for DPLL drivers.
>>>>>> +#
>>>>>> +
>>>>>> +obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>>>
>>>>> What's "sys" and why is it here?
>>>>
>>>> It's an object file for the subsystem. Could be useful if we will have
>>>> drivers
>>>> for DPLL-only devices.
>>>
>>> Yeah, but why "sys"? I don't get what "sys" means here.
>>> Can't this be just "dpll.o"?
>>>
>>>
>>>>
>>>>>> +dpll_sys-y                  += dpll_core.o
>>>>>> +dpll_sys-y                  += dpll_netlink.o
>>>>>> +dpll_sys-y                  += dpll_nl.o
>>>>>> +
>>>
>>> [...]
>>>
>>>
>>>>>> +struct dpll_device *
>>>>>> +dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
>>>>>> *module)
>>>>>> +{
>>>>>> +	struct dpll_device *dpll, *ret = NULL;
>>>>>> +	unsigned long index;
>>>>>> +
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	xa_for_each(&dpll_device_xa, index, dpll) {
>>>>>> +		if (dpll->clock_id == clock_id &&
>>>>>> +		    dpll->dev_driver_id == dev_driver_id &&
>>>>>
>>>>> Why you need "dev_driver_id"? clock_id is here for the purpose of
>>>>> identification, isn't that enough for you.
>>>>
>>>> dev_driver_id is needed to provide several DPLLs from one device. In ice
>>>> driver
>>>> implementation there are 2 different DPLLs - to recover from PPS input and
>>>> to
>>>> recover from Sync-E. I believe there is only one clock, that's why clock id
>>>> is the same for both of them. But Arkadiusz can tell more about it.
>>>
>>> Okay, I see. Clock_id is the same. Could we have index for pin, could
>>> this be index too:
>>>
>>> dpll_device_get(u64 clock_id, u32 device_index, struct module *module);
>>> dpll_pin_get(u64 clock_id, u32 pin_index, struct module *module,
>>> 	     const struct dpll_pin_properties *prop);
>>>
>>> This way it is consistent, driver provides custom index for both dpll
>>> device and dpll pin.
>>>
>>> Makes sense?
>>>
>>
>> IMHO, Yes this better shows the intentions.
> 
> Ok.
> 
> 
>>
>>>
>>>>>
>>>>> Plus, the name is odd. "dev_driver" should certainly be avoided.
>>>>
>>>> Simply id doesn't tell anything either. dpll_dev_id?
>>>
>>> Yeah, see above.
>>>
>>>
>>>>
>>>>>> +		    dpll->module == module) {
>>>>>> +			ret = dpll;
>>>>>> +			refcount_inc(&ret->refcount);
>>>>>> +			break;
>>>>>> +		}
>>>>>> +	}
>>>>>> +	if (!ret)
>>>>>> +		ret = dpll_device_alloc(clock_id, dev_driver_id, module);
>>>>>> +	mutex_unlock(&dpll_device_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;
>>>>>
>>>>> Remove this check. The driver should not call this with NULL.
>>>>
>>>> Well, netdev_put() has this kind of check. As well as spi_dev_put() or
>>>> i2c_put_adapter() at least. Not sure I would like to avoid a bit of safety.
>>>
>>> IDK, maybe for historical reasons. My point is, id driver is callin
>>> this with NULL, there is something odd in the driver flow. Lets not
>>> allow that for new code.
>>>
>>>
>>>>
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	if (refcount_dec_and_test(&dpll->refcount)) {
>>>>>> +		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>>>>
>>>>> ASSERT_DPLL_NOT_REGISTERED(dpll);
>>>>
>>>> Good point!
>>>>
>>>>>> +		xa_destroy(&dpll->pin_refs);
>>>>>> +		xa_erase(&dpll_device_xa, dpll->id);
>>>>>> +		kfree(dpll);
>>>>>> +	}
>>>>>> +	mutex_unlock(&dpll_device_xa_lock);
>>>>>> +}
>>>>>> +EXPORT_SYMBOL_GPL(dpll_device_put);
>>>>>> +
>>>>>> +/**
>>>>>> + * 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,
>>>>>> +			 struct dpll_device_ops *ops, void *priv,
>>>>>> +			 struct device *owner)
>>>>>> +{
>>>>>> +	if (WARN_ON(!ops || !owner))
>>>>>> +		return -EINVAL;
>>>>>> +	if (WARN_ON(type <= DPLL_TYPE_UNSPEC || type > DPLL_TYPE_MAX))
>>>>>> +		return -EINVAL;
>>>>>> +	mutex_lock(&dpll_device_xa_lock);
>>>>>> +	if (ASSERT_DPLL_NOT_REGISTERED(dpll)) {
>>>>>> +		mutex_unlock(&dpll_device_xa_lock);
>>>>>> +		return -EEXIST;
>>>>>> +	}
>>>>>> +	dpll->dev.bus = owner->bus;
>>>>>> +	dpll->parent = owner;
>>>>>> +	dpll->type = type;
>>>>>> +	dpll->ops = ops;
>>>>>> +	dev_set_name(&dpll->dev, "%s_%d", dev_name(owner),
>>>>>> +		     dpll->dev_driver_id);
>>>>>
>>>>> This is really odd. As a result, the user would see something like:
>>>>> pci/0000:01:00.0_1
>>>>> pci/0000:01:00.0_2
>>>>>
>>>>> I have to say it is confusing. In devlink, is bus/name and the user
>>>>> could use this info to look trough sysfs. Here, 0000:01:00.0_1 is not
>>>>> there. Also, "_" might have some meaning on some bus. Should not
>>>>> concatename dev_name() with anything.
>>>>>
>>>>> Thinking about this some more, the module/clock_id tuple should be
>>>>> uniqueue and stable. It is used for dpll_device_get(), it could be used
>>>>> as the user handle, can't it?
>>>>> Example:
>>>>> ice/c92d02a7129f4747
>>>>> mlx5/90265d8bf6e6df56
>>>>>
>>>>> If you really need the "dev_driver_id" (as I believe clock_id should be
>>>>> enough), you can put it here as well:
>>>>> ice/c92d02a7129f4747/1
>>>>> ice/c92d02a7129f4747/2
>>>>>
>>>>
>>>> Looks good, will change it
>>>
>>> Great.
>>>
>>>
>>>>
>>>>> This would also be beneficial for mlx5, as mlx5 with 2 PFs would like to
>>>>> share instance of DPLL equally, there is no "one clock master". >
>>>
>>> [...]
>>>
>>>
>>>>>> +	pin->prop.description = kstrdup(prop->description, GFP_KERNEL);
>>>>>> +	if (!pin->prop.description) {
>>>>>> +		ret = -ENOMEM;
>>>>>> +		goto release;
>>>>>> +	}
>>>>>> +	if (WARN_ON(prop->type <= DPLL_PIN_TYPE_UNSPEC ||
>>>>>> +		    prop->type > DPLL_PIN_TYPE_MAX)) {
>>>>>> +		ret = -EINVAL;
>>>>>> +		goto release;
>>>>>> +	}
>>>>>> +	pin->prop.type = prop->type;
>>>>>> +	pin->prop.capabilities = prop->capabilities;
>>>>>> +	pin->prop.freq_supported = prop->freq_supported;
>>>>>> +	pin->prop.any_freq_min = prop->any_freq_min;
>>>>>> +	pin->prop.any_freq_max = prop->any_freq_max;
>>>>>
>>>>> Make sure that the driver maintains prop (static const) and just save
>>>>> the pointer. Prop does not need to be something driver needs to change.
>>>>>
>>>>
>>>> What's the difference? For ptp_ocp, we have the same configuration for all
>>>> ext pins and the allocator only changes the name of the pin. Properties of
>>>> the DPLL pins are stored within the pin object, not the driver, in this
>>> case.
>>>> Not sure if the pointer way is much better...
>>>
>>> For things like this it is common to have static const array in the
>>> driver, like:
>>>
>>> static const struct dpll_pin_properties dpll_pin_props[] = {
>>> 	{
>>> 		.description = "SMA0",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA1",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA2",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> 	{
>>> 		.description = "SMA3",
>>> 		.freq_supported = DPLL_PIN_FREQ_SUPP_MAX,
>>> 		.type = DPLL_PIN_TYPE_EXT,
>>> 		.any_freq_max = 10000000,
>>> 		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>>> 	},
>>> };
>>>
>>> Here you have very nice list of pins, the reader knows right away what
>>> is happening.
>>>
>>> Thinking about "description" name, I think would be more appropriate to
>>> name this "label" as it represents user-facing label on the connector,
>>> isn't it? Does not describe anything.
>>>
>>
>> "label" seems good.
> 
> Ok.
> 
> 
>>
>>>
>>>>
>>>>>
>>>
>>> [...]
>>>
>>>
>>>>
>>>>>
>>>>>> +	     const struct dpll_pin_properties *prop)
>>>>>> +{
>>>>>> +	struct dpll_pin *pos, *ret = NULL;
>>>>>> +	unsigned long index;
>>>>>> +
>>>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>>>> +	xa_for_each(&dpll_pin_xa, index, pos) {
>>>>>> +		if (pos->clock_id == clock_id &&
>>>>>> +		    pos->dev_driver_id == device_drv_id &&
>>>>>> +		    pos->module == module) {
>>>>>
>>>>> Compare prop as well.
>>>>>
>>>>> Can't the driver_id (pin index) be something const as well? I think it
>>>>> should. And therefore it could be easily put inside.
>>>>>
>>>>
>>>> I think clock_id + dev_driver_id + module should identify the pin exactly.
>>>> And now I think that *prop is not needed here at all. Arkadiusz, any
>>>> thoughts?
>>>
>>> IDK, no strong opinion on this. I just thought it may help to identify
>>> the pin and avoid potential driver bugs. (Like registering 2 pins with
>>> the same properties).
>>>
>>
>> It would make most sense if pin_index would be a part of *prop.
> 
> Hmm. I see one example where it would not be suitable:
> NIC with N physical ports. You, as a driver, register one pin per
> physical port. You have one static const prop and pass the pointer to if
> for every registration call of N pins, each time with different
> pin_index.
> 
> So from what I see, the prop describes a flavour of pin in the driver.
> In the exaple above, it is still the same SyncE pin, with the same ops.
> Only multiple instances of that distinguished by pin_index and priv.
> 
> Makes sense?
> 
> 
>>
>>> [...]
>>>
>>>
>>>>>> +/**
>>>>>> + * 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,
>>>>>> +		  struct dpll_pin_ops *ops, void *priv,
>>>>>> +		  struct device *rclk_device)
>>>>>
>>>>> Wait a second, what is this "struct device *"? Looks very odd.
>>>>>
>>>>>
>>>>>> +{
>>>>>> +	const char *rclk_name = rclk_device ? dev_name(rclk_device) :
>>>>>> NULL;
>>>>>
>>>>> If you need to store something here, store the pointer to the device
>>>>> directly. But this rclk_device seems odd to me.
>>>>> Dev_name is in case of PCI device for example 0000:01:00.0? That alone
>>>>> is incomplete. What should it server for?
>>>>>
>>>>
>>>> Well, these questions go to Arkadiusz...
>>>
>>
>>
>> [ copy paste my answer from previous response ]
> 
> Does not help, see below.
> 
> 
>> If pin is able to recover signal from some device this shall convey that
>> device struct pointer.
> 
> But one device (struct device instance) could easily have multiple
> netdev instances attached and therefore mutiple sources of recovered
> signal.
> 
> mlxsw driver is one of the examples of this 1:N mapping between device
> and netdev.
> 
> 
>> Name of that device is later passed to the user with DPLL_A_PIN_RCLK_DEVICE
>> attribute.
>> Sure we can have pointer to device and use dev_name (each do/dump) on netlink
>> part. But isn't it better to have the name ready to use there?
>>
>> It might be incomplete only if one device would have some kind of access to
>> a different bus? I don't think it is valid use case.
> 
> Very easily, auxiliary_bus. That is actually the case already for mlx5.
> 
> 
>>
>> Basically the driver will refer only to the devices handled by that driver,
>> which means if dpll is on some bus, also all the pins are there, didn't notice
>> the need to have bus here as well.
> 
> What is the motivation exactly? Is it only SyncE? In case it is, the
> link to the pin should be added from the other side, carried over
> RTNetlink msg of a netdev associated with pin. I will add a patch for
> this.
> 
> Do you need to expose any other recovered clock device source?
> 
> 
>>
>>> Okay.
>>>
>>> [...]
>>>
>>>
>>>>>> + * dpll_pin_get_by_idx - find a pin ref on dpll by pin index
>>>>>> + * @dpll: dpll device pointer
>>>>>> + * @idx: index of pin
>>>>>> + *
>>>>>> + * Find a reference to a pin registered with given dpll and return
>>>>>> its pointer.
>>>>>> + *
>>>>>> + * Return:
>>>>>> + * * valid pointer if pin was found
>>>>>> + * * NULL if not found
>>>>>> + */
>>>>>> +struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, u32 idx)
>>>>>> +{
>>>>>> +	struct dpll_pin_ref *pos;
>>>>>> +	unsigned long i;
>>>>>> +
>>>>>> +	xa_for_each(&dpll->pin_refs, i, pos) {
>>>>>> +		if (pos && pos->pin && pos->pin->dev_driver_id == idx)
>>>>>
>>>>> How exactly pos->pin could be NULL?
>>>>>
>>>>> Also, you are degrading the xarray to a mere list here with lookup like
>>>>> this. Why can't you use the pin index coming from driver and
>>>>> insert/lookup based on this index?
>>>>>
>>>> Good point. We just have to be sure, that drivers provide 0-based indexes for
>>>> their pins. I'll re-think it.
>>>
>>> No, driver can provide indexing which is completely up to his decision.
>>> You should use xa_insert() to insert the entry at specific index. See
>>> devl_port_register for inspiration where it is done exactly like this.
>>>
>>> And this should be done in exactly the same way for both pin and device.
>>>
>>
>> Yes, I agree seems doable and better then it is now.
>>
>> [...]
>>
>>>>>> +static int
>>>>>> +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>>>>>> *dpll)
>>>>>> +{
>>>>>> +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>>>>
>>>>> Why exactly do we need this dua--handle scheme? Why do you need
>>>>> unpredictable DPLL_A_ID to be exposed to userspace?
>>>>> It's just confusing.
>>>>>
>>>> To be able to work with DPLL per integer after iterator on the list deducts
>>>> which DPLL device is needed. It can reduce the amount of memory copies and
>>>> simplify comparisons. Not sure why it's confusing.
>>>
>>> Wait, I don't get it. Could you please explain a bit more?
>>>
>>> My point is, there should be not such ID exposed over netlink
>>> You don't need to expose it to userspace. The user has well defined
>>> handle as you agreed with above. For example:
>>>
>>> ice/c92d02a7129f4747/1
>>> ice/c92d02a7129f4747/2
>>>
>>> This is shown in dpll device GET/DUMP outputs.
>>> Also user passes it during SET operation:
>>> $ dplltool set ice/c92d02a7129f4747/1 mode auto
>>>
>>> Isn't that enough stable and nice?
>>>
>>
>> I agree with Vadim, this is rather to be used by a daemon tools, which
>> would get the index once, then could use it as long as device is there.
> 
> So basically you say, you can have 2 approaches in app:
> 1)
> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> 2):
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> 
> What is exactly benefit of the first one? Why to have 2 handles? Devlink
> is a nice example of 2) approach, no problem there.
> 
> Perhaps I'm missing something, but looks like you want the id for no
> good reason and this dual-handle scheme just makes things more
> complicated with 0 added value.

I would like to avoid any extra memory copies or memory checks when it's 
possible to compare single u32/u64 index value. I might be invisible on 
a single host setup, but running monitoring at scale which will parse 
and compare string on every get/event can burn a bit of compute capacity.

> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +		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_pins_on_pin(struct sk_buff *msg, struct dpll_pin *pin,
>>>>>> +			 struct netlink_ext_ack *extack)
>>>>>> +{
>>>>>> +	struct dpll_pin_ref *ref = NULL;
>>>>>
>>>>> Why this needs to be initialized?
>>>>>
>>>> No need, fixed.
>>>>
>>>>
>>>>>
>>>>>> +	enum dpll_pin_state state;
>>>>>> +	struct nlattr *nest;
>>>>>> +	unsigned long index;
>>>>>> +	int ret;
>>>>>> +
>>>>>> +	xa_for_each(&pin->parent_refs, index, ref) {
>>>>>> +		if (WARN_ON(!ref->ops->state_on_pin_get))
>>>>>> +			return -EFAULT;
>>>>>> +		ret = ref->ops->state_on_pin_get(pin, ref->pin, &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,
>>>>>> +				ref->pin->dev_driver_id)) {
>>>>>> +			ret = -EMSGSIZE;
>>>>>> +			goto nest_cancel;
>>>>>> +		}
>>>>>> +		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>>>>> +			ret = -EMSGSIZE;
>>>>>> +			goto nest_cancel;
>>>>>> +		}
>>>>>> +		nla_nest_end(msg, nest);
>>>>>> +	}
>>>>>
>>>>> How is this function different to dpll_msg_add_pin_parents()?
>>>>> Am I lost? To be honest, this x_on_pin/dpll, parent, refs dance is quite
>>>>> hard to follow for me :/
>>>>>
>>>>> Did you get lost here as well? If yes, this needs some serious think
>>>>> through :)
>>>>>
>>>>
>>>> Let's re-think it again. Arkadiuzs, do you have clear explanation of the
>>>> relationship between these things?
>>>
>>
>> [ copy paste my answer from previous response ]
>> No, it is just leftover I didn't catch, we can leave one function and use it in
>> both cases. Sorry about that, great catch!
> 
> Hmm, ok. I'm still lost a bit with all the referencing and cross
> referencing, I wonder if it could be made a bit simpler and/or easier to
> read and follow.
> 
> 
>>
>> [...]
>>
>>>>>> +	if (nla_put_u32(msg, DPLL_A_PIN_IDX, pin->dev_driver_id))
>>>>>
>>>>> Is it "ID" or "INDEX" (IDX). Please make this consistent in the whole
>>>>> code.
>>>>>
>>>>
>>>> I believe it's INDEX which is provided by the driver. I'll think about
>>>> renaming,
>>>> but suggestions are welcome.
>>>
>>> Let's use "index" and "INDEX" internalla and in Netlink attr names as
>>> well then.
>>>
>>
>> For me makes sense to have a common name instead of origin-based one.
> 
> What do you mean by this?
> 
> 
>>
>>> [...]
>>>
>>>
>>>>
>>>>>
>>>>>> +	int rem, ret = -EINVAL;
>>>>>> +	struct nlattr *a;
>>>>>> +
>>>>>> +	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>>>>> +			  genlmsg_len(info->genlhdr), rem) {
>>>>>
>>>>> This is odd. Why you iterace over attrs? Why don't you just access them
>>>>> directly, like attrs[DPLL_A_PIN_FREQUENCY] for example?
>>>>>
>>>>
>>>> I had some unknown crashes when I was using such access. I might have lost
>>>> some checks, will try it again.
>>>
>>> Odd, yet definitelly debuggable though :)
>>>
>>> [...]
>>>
>>>
>>>>>> +int dpll_pin_pre_dumpit(struct netlink_callback *cb)
>>>>>> +{
>>>>>> +	mutex_lock(&dpll_pin_xa_lock);
>>>>>
>>>>> ABBA deadlock here, see dpll_pin_register() for example where the lock
>>>>> taking order is opposite.
>>>>>
>>>>
>>>> Now I see an ABBA deadlock here, as well as in function before. Not sure
>>>> how to
>>>> solve it here. Any thoughts?
>>>
>>> Well, here you can just call dpll_pre_dumpit() before
>>> mutex_lock(&dpll_pin_xa_lock)
>>> to take the locks in the same order.
>>>
>>
>> This double lock doesn't really improve anything.
>> Any objections on having single mutex/lock for access the dpll subsystem?
> 
> Yeah, we had it in devlink and then we spent a lot of a time to get rid
> of it. Now we have per-instance locking. Here, it would be
> per-dpll-device locking.
> 
> No strong opinion, perhaps on case of dpll one master lock is enough.
> Perhaps Jakub would have some opinion on this.
> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	return dpll_pre_dumpit(cb);
>>>>>> +}
>>>>>> +
>>>>>> +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>>>>>> +{
>>>>>> +	mutex_unlock(&dpll_pin_xa_lock);
>>>>>> +
>>>>>> +	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-
>>>>>> dev_driver_id))
>>>>>> +		return -EMSGSIZE;
>>>>>
>>>>> I don't really understand why you are trying figure something new and
>>>>> interesting with the change notifications. This object mix and random
>>>>> attrs fillup is something very wrong and makes userspace completely
>>>>> fuzzy about what it is getting. And yet it is so simple:
>>>>> You have 2 objects, dpll and pin, please just have:
>>>>> dpll_notify()
>>>>> dpll_pin_notify()
>>>>> and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>>>>> No need for any smartness. Have this dumb and simple.
>>>>>
>>>>> Think about it more as about "object-state-snapshot" than "atomic-change"
>>>>
>>>> But with full object-snapshot user space app will lose the information about
>>>> what exactly has changed. The reason to have this event is to provide the
>>>> attributes which have changed. Otherwise, the app should have full snapshot
>>>> and
>>>> compare all attributes to figure out changes and that's might not be great
>>>> idea.
>>>
>>> Wait, are you saying that the app is stateless? Could you provide
>>> example use cases?
>>>
>> >From what I see, the app managing dpll knows the state of the device and
>>> pins, it monitors for the changes and saves new state with appropriate
>>> reaction (might be some action or maybe just log entry).
>>>
>>
>> It depends on the use case, right? App developer having those information knows
>> what has changed, thus can react in a way it thinks is most suitable.
>> IMHO from user perspective it is good to have a notification which actually
>> shows it's reason, so proper flow could be assigned to handle the reaction.
> 
> Again, could you provide me specific example in which this is needed?
> I may be missing something, but I don't see how it can bring
> and benefit. It just makes the live of the app harder because it has to
> treat the get and notify messages differently.
> 
> It is quite common for app to:
> init:
> 1) get object state
> 2) store it
> 3) apply configuration
> runtime:
> 1) listen to object state change
> 2) store it
> 3) apply configuration
> 
> Same code for both.

Well, I'm thinking about simple monitoring app which will wait for the 
events and create an alert if the changes coming from the event differ 
from the "allowed configs". In this case no real reason to store whole 
object state, but the changes in events are very useful.

> 
> 
> 
> 
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	switch (attr) {
>>>>>> +	case DPLL_A_MODE:
>>>>>> +		ret = dpll_msg_add_mode(msg, dpll, NULL);
>>>>>> +		break;
>>>>>> +	case DPLL_A_SOURCE_PIN_IDX:
>>>>>> +		ret = dpll_msg_add_source_pin_idx(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:
>>>>>> +		ret = dpll_msg_add_pin_freq(msg, pin, NULL, false);
>>>>>> +		break;
>>>>>> +	case DPLL_A_PIN_PRIO:
>>>>>> +		ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>>>> +		if (!ref)
>>>>>> +			return -EFAULT;
>>>>>> +		ret = dpll_msg_add_pin_prio(msg, pin, ref, NULL);
>>>>>> +		break;
>>>>>> +	case DPLL_A_PIN_STATE:
>>>>>> +		if (parent) {
>>>>>> +			ref = dpll_xa_ref_pin_find(&pin->parent_refs, parent);
>>>>>> +			if (!ref)
>>>>>> +				return -EFAULT;
>>>>>> +			if (!ref->ops || !ref->ops->state_on_pin_get)
>>>>>> +				return -EOPNOTSUPP;
>>>>>> +			ret = ref->ops->state_on_pin_get(pin, parent, &state,
>>>>>> +							 NULL);
>>>>>> +			if (ret)
>>>>>> +				return ret;
>>>>>> +			if (nla_put_u32(msg, DPLL_A_PIN_PARENT_IDX,
>>>>>> +					parent->dev_driver_id))
>>>>>> +				return -EMSGSIZE;
>>>>>> +		} else {
>>>>>> +			ref = dpll_xa_ref_dpll_find(&pin->dpll_refs, dpll);
>>>>>> +			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);
>>>>>
>>>>> I don't really get it. Why exactly you keep having this *EVENT* cmds?
>>>>> Why per-object NEW/GET/DEL cmds shared with get genl op are not enough?
>>>>> I have to be missing something.
>>>>
>>>> Changes might come from other places, but will affect the DPLL device and we
>>>> have to notify users in this case.
>>>
>>> I'm not sure I follow. There are 2 scenarios for change:
>>> 1) user originated - user issues set of something
>>> 2) driver originated - something changes in HW, driver propagates that
>>>
>>> With what I suggest, both scenarios work of course. My point is, user
>>> knows very well the objects: device and pin, he knows the format or
>>> messages that are related to GET/DUMP/SET operations of both. The
>>> notification should have the same format, that is all I say.
>>>
>>> Btw, you can see devlink code for example how the notifications like
>>> this are implemented and work.
>>>
>>
>> Devlink packs all the info into a netlink message and notifies with it, isn't
>> it that it has all the info "buffered" in its structures?
> 
> Not really. Ops are there as well.
> 
> 
>> A dpll subsystem keeps only some of the info in its internal structures, but
>> for most of it it has to question the driver. It would do it multiple times
>> i.e. if there would be a change on a pin connected to multiple dpll devices.
>>
>> With current approach the notifications are pretty light to the subsystem as
>> well as for the registered clients, and as you suggested with having info of
>> what actually changed the userspace could implement a stateless daemon right
>> away.
> 
> Okay, examples? I really can't see how this stateless deamon could be
> implemented, but perhaps I lack better imagination :/
> 
> 
>>
>> Thank you,
>> Arkadiusz


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

* Re: [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
  2023-03-26 17:00   ` Jiri Pirko
@ 2023-03-28 16:36     ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-28 16:36 UTC (permalink / raw)
  To: Jiri Pirko, netdev, arkadiusz.kubalewski, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

On 26/03/2023 18:00, Jiri Pirko wrote:
> From: Jiri Pirko <jiri@nvidia.com>
> 
> Hi.
> 
> This is extending your patchset. Basically, I do this on top of the
> changes I pointed out during review. For example patch #6 is exposing
> pin handle which is going to change, etc (there, I put a note).
> 
> First 5 patches are just needed dependencies and you can squash them
> into your patch/patches. Last two patches should go in separatelly.
> 
> Please note that the patch #6 is replacing the need to pass the rclk
> device during pin registration by putting a link between netdev and dpll
> pin.
> 
> Please merge this into your dpll patchset and include it in the next
> RFC. Thanks!
>

Hi Jiri!

Thanks for the patch set. It looks like it covers some changes that I 
have also done to address the comments. I'll try to combine everything 
in a couple of days and will re-spin series and we restart review 
process. I think that there are open question still in the conversation 
which were not answered.

Best,
Vadim

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

* Re: [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
@ 2023-03-28 16:36     ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-28 16:36 UTC (permalink / raw)
  To: Jiri Pirko, netdev, arkadiusz.kubalewski, vadfed
  Cc: kuba, jonathan.lemon, pabeni, poros, mschmidt, linux-arm-kernel,
	linux-clk

On 26/03/2023 18:00, Jiri Pirko wrote:
> From: Jiri Pirko <jiri@nvidia.com>
> 
> Hi.
> 
> This is extending your patchset. Basically, I do this on top of the
> changes I pointed out during review. For example patch #6 is exposing
> pin handle which is going to change, etc (there, I put a note).
> 
> First 5 patches are just needed dependencies and you can squash them
> into your patch/patches. Last two patches should go in separatelly.
> 
> Please note that the patch #6 is replacing the need to pass the rclk
> device during pin registration by putting a link between netdev and dpll
> pin.
> 
> Please merge this into your dpll patchset and include it in the next
> RFC. Thanks!
>

Hi Jiri!

Thanks for the patch set. It looks like it covers some changes that I 
have also done to address the comments. I'll try to combine everything 
in a couple of days and will re-spin series and we restart review 
process. I think that there are open question still in the conversation 
which were not answered.

Best,
Vadim

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-15 12:24         ` Jiri Pirko
@ 2023-03-31 23:28           ` Vadim Fedorenko
  -1 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-31 23:28 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On 15.03.2023 12:24, Jiri Pirko wrote:
> Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>> On 14/03/2023 10:05, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>>>> 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>>>> 2 files changed, 200 insertions(+), 10 deletions(-)
>>>>
>>>> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>>>> index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>>>> --- a/drivers/ptp/ptp_ocp.c
>>>> +++ b/drivers/ptp/ptp_ocp.c
>>>> @@ -23,6 +23,8 @@
>>>> #include <linux/mtd/mtd.h>
>>>> #include <linux/nvmem-consumer.h>
>>>> #include <linux/crc16.h>
>>>> +#include <linux/dpll.h>
>>>> +#include <uapi/linux/dpll.h>
>>>
>>> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
>>>
>>
>> Ah, yeah, you've mentioned it for ice driver last time, but I forgot to check
>> it here. Removed.
>>
>>>
>>>>
>>>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>>>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>>>> @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>>>> 	bool	fixed_dir;
>>>> 	bool	disabled;
>>>> 	u8	default_fcn;
>>>> +	struct dpll_pin *dpll_pin;
>>>> };
>>>>
>>>> struct ocp_attr_group {
>>>> @@ -353,6 +356,7 @@ struct ptp_ocp {
>>>> 	struct ptp_ocp_signal	signal[4];
>>>> 	struct ptp_ocp_sma_connector sma[4];
>>>
>>> It is quite customary to use defines for const numbers like this. Why
>>> don't you do that in ptp_ocp?
>>
>> Historical reasons. Jonathan might answer this question.
>> I will add it to my TODO list for the driver.
>>

Added macros for SMAs for now.

>>>> 	const struct ocp_sma_op *sma_op;
>>>> +	struct dpll_device *dpll;
>>>> };
>>>>
>>>> #define OCP_REQ_TIMESTAMP	BIT(0)
>>>> @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>>>> }
>>>>
>>>> static int
>>>> -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>>>> {
>>>> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>>>> -	enum ptp_ocp_sma_mode mode;
>>>> -	int val;
>>>> -
>>>> -	mode = sma->mode;
>>>> -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>>>> -	if (val < 0)
>>>> -		return val;
>>>>
>>>> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>>>> 		return -EOPNOTSUPP;
>>>> @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> 	return val;
>>>> }
>>>>
>>>> +static int
>>>> +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> +{
>>>> +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>>>> +	enum ptp_ocp_sma_mode mode;
>>>> +	int val;
>>>> +
>>>> +	mode = sma->mode;
>>>> +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>>>> +	if (val < 0)
>>>> +		return val;
>>>> +
>>>> +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>>>> +}
>>>> +
>>>> static ssize_t
>>>> sma1_store(struct device *dev, struct device_attribute *attr,
>>>> 	   const char *buf, size_t count)
>>>> @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>>>> 	device_unregister(&bp->dev);
>>>> }
>>>>
>>>> +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>>>> +{
>>>> +	int i;
>>>> +
>>>> +	for (i = 0; i < 4; i++) {
>>>> +		if (bp->sma[i].dpll_pin == pin)
>>>> +			return i;
>>>
>>> Just pass &bp->sma[i] as a priv to dpll_pin_register().
>>> and get that pointer directly in pin ops. No need for lookup and use of
>>> sma_nr at all.
>>
>> I'm still thinking about the change that you proposed to remove these
>> _priv() helpers. I have to look into ice code to be sure we won't break
>> any assumptions in it with moving to additional (void *) parameter.
> 
> There are basically 2 ways:
> someop(struct subsystemobj *x, void *priv)
> {
> 	struct *mine = priv;
> }
> or:
> someop(struct subsystemobj *x)
> {
> 	struct *mine = subsystem_get_priv(x);
> }
> 
> Both are more or less equal. The first has benefit that the caller most
> usually has direct access to the priv, so it can just easily pass it on.
> Also, you as the driver write see right away there is a priv arg and
> makes you want to use it and not figure out odd lookups to get to the
> same priv.

Thinking about this part. We have tons of parameters for some ops already. 
Adding void *priv to every one of them (and actually two for pins) makes them 
even more ugly. Let's stay with helpers if you don't have strong opinion against.


>>>> +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>>>> +			      const struct dpll_device *dpll,
>>>> +			      u32 *frequency,
>>>> +			      struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>>>> +	u32 val;
>>>> +
>>>> +	if (sma_nr < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	val = bp->sma_op->get(bp, sma_nr);
>>>> +	if (!val)
>>>> +		*frequency = 1000000;
>>>> +	else
>>>> +		*frequency = 0;
>>>
>>> I don't follow. How the frequency could be 0? Does that mean that the
>>> pin is disabled? If yes, you should rather implement
>>> .state_on_dpll_get
>>> .state_on_dpll_set
>>>
>>> and leave the frequency constant.
>>
>> It's actually a mistake. It should be 1 which means 1Hz, PPS. The value
>> returned by sma_op->get is actually the type of the signal where 0 is 1PPS, 1
>> is 10Mhz and so on.
> 
> So you support freq change? In the comment above you wrote "Currently
> hardware supports fixed frequencies only". I'm confused.
> The point is:
> 1) if you support freq change, you should sanitize the input properly in
>     frequency_set() and return correct actual value in frequency_get()
> 2) if you don't support freq change, avoid filling-up these ops
>     entirely.

Improved this part completely.

>>>> 	struct ptp_ocp *bp;
>>>> -	int err;
>>>> +	int err, i;
>>>> +	u64 clkid;
>>>>
>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>>>> 	if (!devlink) {
>>>> @@ -4226,8 +4377,44 @@ 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 (!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;
>>>> +
>>>> +	prop.description = &sma[0];
>>>> +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>>>> +	prop.type = DPLL_PIN_TYPE_EXT;
>>>> +	prop.any_freq_max = 10000000;
>>>> +	prop.any_freq_min = 0;
>>>> +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>>>> +
>>>> +	for (i = 0; i < 4; i++) {
>>>> +		sma[3] = 0x31 + i;
>>>
>>> Ugh. Just use the static const array as I suggested in the dpll patch
>>> reply. Helps you avoid this sort of "magic".
>>>
>> well, yes. but at the same time Arkadiusz raised a good question about
>> accessing driver's memory from the subsystem code - doesn't look clean.
> 
> It is completely clean and we do it everywhere in the kernel.
> No problem what so ever.
>

Did it this way.

> 
>>
>>>
>>>> +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>>>> +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
>>>
>>> How it could be NULL?
>>>
>>
>> Allocation fail?
> 
> No? I mean, I don't see such possibility of return value of dpll_pin_get()
> Do you see it or are you just guessing? :)
> 

Fixed this part.

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-03-31 23:28           ` Vadim Fedorenko
  0 siblings, 0 replies; 202+ messages in thread
From: Vadim Fedorenko @ 2023-03-31 23:28 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

On 15.03.2023 12:24, Jiri Pirko wrote:
> Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>> On 14/03/2023 10:05, Jiri Pirko wrote:
>>> Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:
>>>> 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 | 209 ++++++++++++++++++++++++++++++++++++++++--
>>>> 2 files changed, 200 insertions(+), 10 deletions(-)
>>>>
>>>> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>>>> index fe4971b65c64..8c4cfabc1bfa 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 4bbaccd543ad..02c95e724ec8 100644
>>>> --- a/drivers/ptp/ptp_ocp.c
>>>> +++ b/drivers/ptp/ptp_ocp.c
>>>> @@ -23,6 +23,8 @@
>>>> #include <linux/mtd/mtd.h>
>>>> #include <linux/nvmem-consumer.h>
>>>> #include <linux/crc16.h>
>>>> +#include <linux/dpll.h>
>>>> +#include <uapi/linux/dpll.h>
>>>
>>> Don't include UAPI directly. I'm pretty sure I had this comment earlier.
>>>
>>
>> Ah, yeah, you've mentioned it for ice driver last time, but I forgot to check
>> it here. Removed.
>>
>>>
>>>>
>>>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>>>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>>>> @@ -267,6 +269,7 @@ struct ptp_ocp_sma_connector {
>>>> 	bool	fixed_dir;
>>>> 	bool	disabled;
>>>> 	u8	default_fcn;
>>>> +	struct dpll_pin *dpll_pin;
>>>> };
>>>>
>>>> struct ocp_attr_group {
>>>> @@ -353,6 +356,7 @@ struct ptp_ocp {
>>>> 	struct ptp_ocp_signal	signal[4];
>>>> 	struct ptp_ocp_sma_connector sma[4];
>>>
>>> It is quite customary to use defines for const numbers like this. Why
>>> don't you do that in ptp_ocp?
>>
>> Historical reasons. Jonathan might answer this question.
>> I will add it to my TODO list for the driver.
>>

Added macros for SMAs for now.

>>>> 	const struct ocp_sma_op *sma_op;
>>>> +	struct dpll_device *dpll;
>>>> };
>>>>
>>>> #define OCP_REQ_TIMESTAMP	BIT(0)
>>>> @@ -2689,16 +2693,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>>>> }
>>>>
>>>> static int
>>>> -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>>>> {
>>>> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>>>> -	enum ptp_ocp_sma_mode mode;
>>>> -	int val;
>>>> -
>>>> -	mode = sma->mode;
>>>> -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>>>> -	if (val < 0)
>>>> -		return val;
>>>>
>>>> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>>>> 		return -EOPNOTSUPP;
>>>> @@ -2733,6 +2730,21 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> 	return val;
>>>> }
>>>>
>>>> +static int
>>>> +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>>>> +{
>>>> +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>>>> +	enum ptp_ocp_sma_mode mode;
>>>> +	int val;
>>>> +
>>>> +	mode = sma->mode;
>>>> +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>>>> +	if (val < 0)
>>>> +		return val;
>>>> +
>>>> +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>>>> +}
>>>> +
>>>> static ssize_t
>>>> sma1_store(struct device *dev, struct device_attribute *attr,
>>>> 	   const char *buf, size_t count)
>>>> @@ -4171,12 +4183,151 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>>>> 	device_unregister(&bp->dev);
>>>> }
>>>>
>>>> +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>>>> +{
>>>> +	int i;
>>>> +
>>>> +	for (i = 0; i < 4; i++) {
>>>> +		if (bp->sma[i].dpll_pin == pin)
>>>> +			return i;
>>>
>>> Just pass &bp->sma[i] as a priv to dpll_pin_register().
>>> and get that pointer directly in pin ops. No need for lookup and use of
>>> sma_nr at all.
>>
>> I'm still thinking about the change that you proposed to remove these
>> _priv() helpers. I have to look into ice code to be sure we won't break
>> any assumptions in it with moving to additional (void *) parameter.
> 
> There are basically 2 ways:
> someop(struct subsystemobj *x, void *priv)
> {
> 	struct *mine = priv;
> }
> or:
> someop(struct subsystemobj *x)
> {
> 	struct *mine = subsystem_get_priv(x);
> }
> 
> Both are more or less equal. The first has benefit that the caller most
> usually has direct access to the priv, so it can just easily pass it on.
> Also, you as the driver write see right away there is a priv arg and
> makes you want to use it and not figure out odd lookups to get to the
> same priv.

Thinking about this part. We have tons of parameters for some ops already. 
Adding void *priv to every one of them (and actually two for pins) makes them 
even more ugly. Let's stay with helpers if you don't have strong opinion against.


>>>> +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>>>> +			      const struct dpll_device *dpll,
>>>> +			      u32 *frequency,
>>>> +			      struct netlink_ext_ack *extack)
>>>> +{
>>>> +	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>> +	int sma_nr = ptp_ocp_dpll_pin_to_sma(bp, pin);
>>>> +	u32 val;
>>>> +
>>>> +	if (sma_nr < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	val = bp->sma_op->get(bp, sma_nr);
>>>> +	if (!val)
>>>> +		*frequency = 1000000;
>>>> +	else
>>>> +		*frequency = 0;
>>>
>>> I don't follow. How the frequency could be 0? Does that mean that the
>>> pin is disabled? If yes, you should rather implement
>>> .state_on_dpll_get
>>> .state_on_dpll_set
>>>
>>> and leave the frequency constant.
>>
>> It's actually a mistake. It should be 1 which means 1Hz, PPS. The value
>> returned by sma_op->get is actually the type of the signal where 0 is 1PPS, 1
>> is 10Mhz and so on.
> 
> So you support freq change? In the comment above you wrote "Currently
> hardware supports fixed frequencies only". I'm confused.
> The point is:
> 1) if you support freq change, you should sanitize the input properly in
>     frequency_set() and return correct actual value in frequency_get()
> 2) if you don't support freq change, avoid filling-up these ops
>     entirely.

Improved this part completely.

>>>> 	struct ptp_ocp *bp;
>>>> -	int err;
>>>> +	int err, i;
>>>> +	u64 clkid;
>>>>
>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>>>> 	if (!devlink) {
>>>> @@ -4226,8 +4377,44 @@ 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 (!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;
>>>> +
>>>> +	prop.description = &sma[0];
>>>> +	prop.freq_supported = DPLL_PIN_FREQ_SUPP_MAX;
>>>> +	prop.type = DPLL_PIN_TYPE_EXT;
>>>> +	prop.any_freq_max = 10000000;
>>>> +	prop.any_freq_min = 0;
>>>> +	prop.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>>>> +
>>>> +	for (i = 0; i < 4; i++) {
>>>> +		sma[3] = 0x31 + i;
>>>
>>> Ugh. Just use the static const array as I suggested in the dpll patch
>>> reply. Helps you avoid this sort of "magic".
>>>
>> well, yes. but at the same time Arkadiusz raised a good question about
>> accessing driver's memory from the subsystem code - doesn't look clean.
> 
> It is completely clean and we do it everywhere in the kernel.
> No problem what so ever.
>

Did it this way.

> 
>>
>>>
>>>> +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &prop);
>>>> +		if (IS_ERR_OR_NULL(bp->sma[i].dpll_pin)) {
>>>
>>> How it could be NULL?
>>>
>>
>> Allocation fail?
> 
> No? I mean, I don't see such possibility of return value of dpll_pin_get()
> Do you see it or are you just guessing? :)
> 

Fixed this part.

_______________________________________________
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] 202+ messages in thread

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

Tue, Mar 28, 2023 at 05:22:04PM CEST, vadim.fedorenko@linux.dev wrote:
>On 15/03/2023 09:22, Jiri Pirko wrote:
>> Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>> > > From: Jiri Pirko <jiri@resnulli.us>
>> > > Sent: Tuesday, March 14, 2023 10:22 AM
>> > > 
>> > > Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>> > > > On 13.03.2023 16:21, Jiri Pirko wrote:
>> > > > > Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>> > > > > > +static int
>> > > > > > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>> > > > > > *dpll)
>> > > > > > +{
>> > > > > > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>> > > > > 
>> > > > > Why exactly do we need this dua--handle scheme? Why do you need
>> > > > > unpredictable DPLL_A_ID to be exposed to userspace?
>> > > > > It's just confusing.
>> > > > > 
>> > > > To be able to work with DPLL per integer after iterator on the list deducts
>> > > > which DPLL device is needed. It can reduce the amount of memory copies and
>> > > > simplify comparisons. Not sure why it's confusing.
>> > > 
>> > > Wait, I don't get it. Could you please explain a bit more?
>> > > 
>> > > My point is, there should be not such ID exposed over netlink
>> > > You don't need to expose it to userspace. The user has well defined
>> > > handle as you agreed with above. For example:
>> > > 
>> > > ice/c92d02a7129f4747/1
>> > > ice/c92d02a7129f4747/2
>> > > 
>> > > This is shown in dpll device GET/DUMP outputs.
>> > > Also user passes it during SET operation:
>> > > $ dplltool set ice/c92d02a7129f4747/1 mode auto
>> > > 
>> > > Isn't that enough stable and nice?
>> > > 
>> > 
>> > I agree with Vadim, this is rather to be used by a daemon tools, which
>> > would get the index once, then could use it as long as device is there.
>> 
>> So basically you say, you can have 2 approaches in app:
>> 1)
>> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> 2):
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> 
>> What is exactly benefit of the first one? Why to have 2 handles? Devlink
>> is a nice example of 2) approach, no problem there.
>> 
>> Perhaps I'm missing something, but looks like you want the id for no
>> good reason and this dual-handle scheme just makes things more
>> complicated with 0 added value.
>
>I would like to avoid any extra memory copies or memory checks when it's
>possible to compare single u32/u64 index value. I might be invisible on a
>single host setup, but running monitoring at scale which will parse and
>compare string on every get/event can burn a bit of compute capacity.

Wait, that does not make any sense what so ever.
Show me numbers and real usecase. A sane app gets once at start, then
processes notifications. You have a flow where string compare on a
netlink command makes difference to int compare? Like tens of thousands
of netlink cmds per second? I doubt that. If yes, it is a misuse. Btw,
the string compare would be your last problem comparing the overhead of
Netlink processing with ioctl for example.

Your dual handle scheme just adds complexicity, confusion with 0 added
value. Please drop it. I really don't understand the need to defend this
odd approach :/

[...]


>> > > > > > +
>> > > > > > +	return dpll_pre_dumpit(cb);
>> > > > > > +}
>> > > > > > +
>> > > > > > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>> > > > > > +{
>> > > > > > +	mutex_unlock(&dpll_pin_xa_lock);
>> > > > > > +
>> > > > > > +	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-
>> > > > > > dev_driver_id))
>> > > > > > +		return -EMSGSIZE;
>> > > > > 
>> > > > > I don't really understand why you are trying figure something new and
>> > > > > interesting with the change notifications. This object mix and random
>> > > > > attrs fillup is something very wrong and makes userspace completely
>> > > > > fuzzy about what it is getting. And yet it is so simple:
>> > > > > You have 2 objects, dpll and pin, please just have:
>> > > > > dpll_notify()
>> > > > > dpll_pin_notify()
>> > > > > and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>> > > > > No need for any smartness. Have this dumb and simple.
>> > > > > 
>> > > > > Think about it more as about "object-state-snapshot" than "atomic-change"
>> > > > 
>> > > > But with full object-snapshot user space app will lose the information about
>> > > > what exactly has changed. The reason to have this event is to provide the
>> > > > attributes which have changed. Otherwise, the app should have full snapshot
>> > > > and
>> > > > compare all attributes to figure out changes and that's might not be great
>> > > > idea.
>> > > 
>> > > Wait, are you saying that the app is stateless? Could you provide
>> > > example use cases?
>> > > 
>> > >From what I see, the app managing dpll knows the state of the device and
>> > > pins, it monitors for the changes and saves new state with appropriate
>> > > reaction (might be some action or maybe just log entry).
>> > > 
>> > 
>> > It depends on the use case, right? App developer having those information knows
>> > what has changed, thus can react in a way it thinks is most suitable.
>> > IMHO from user perspective it is good to have a notification which actually
>> > shows it's reason, so proper flow could be assigned to handle the reaction.
>> 
>> Again, could you provide me specific example in which this is needed?
>> I may be missing something, but I don't see how it can bring
>> and benefit. It just makes the live of the app harder because it has to
>> treat the get and notify messages differently.
>> 
>> It is quite common for app to:
>> init:
>> 1) get object state
>> 2) store it
>> 3) apply configuration
>> runtime:
>> 1) listen to object state change
>> 2) store it
>> 3) apply configuration
>> 
>> Same code for both.
>
>Well, I'm thinking about simple monitoring app which will wait for the events
>and create an alert if the changes coming from the event differ from the
>"allowed configs". In this case no real reason to store whole object state,
>but the changes in events are very useful.

No problem. But as I wrote elsewhere in this thread, make sure that msg
format of a change message is the same as the format of get cmd message.
Basically think of it as a get cmd message filtered to have only
attrs which changed. Same nesting and everything. Makes it simple for
the app to have same parsing code for get/dump/notification messages.

[...]

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

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

Tue, Mar 28, 2023 at 05:22:04PM CEST, vadim.fedorenko@linux.dev wrote:
>On 15/03/2023 09:22, Jiri Pirko wrote:
>> Tue, Mar 14, 2023 at 06:50:57PM CET, arkadiusz.kubalewski@intel.com wrote:
>> > > From: Jiri Pirko <jiri@resnulli.us>
>> > > Sent: Tuesday, March 14, 2023 10:22 AM
>> > > 
>> > > Mon, Mar 13, 2023 at 11:59:32PM CET, vadim.fedorenko@linux.dev wrote:
>> > > > On 13.03.2023 16:21, Jiri Pirko wrote:
>> > > > > Sun, Mar 12, 2023 at 03:28:03AM CET, vadfed@meta.com wrote:

[...]


>> > > > > > +static int
>> > > > > > +dpll_msg_add_dev_handle(struct sk_buff *msg, const struct dpll_device
>> > > > > > *dpll)
>> > > > > > +{
>> > > > > > +	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>> > > > > 
>> > > > > Why exactly do we need this dua--handle scheme? Why do you need
>> > > > > unpredictable DPLL_A_ID to be exposed to userspace?
>> > > > > It's just confusing.
>> > > > > 
>> > > > To be able to work with DPLL per integer after iterator on the list deducts
>> > > > which DPLL device is needed. It can reduce the amount of memory copies and
>> > > > simplify comparisons. Not sure why it's confusing.
>> > > 
>> > > Wait, I don't get it. Could you please explain a bit more?
>> > > 
>> > > My point is, there should be not such ID exposed over netlink
>> > > You don't need to expose it to userspace. The user has well defined
>> > > handle as you agreed with above. For example:
>> > > 
>> > > ice/c92d02a7129f4747/1
>> > > ice/c92d02a7129f4747/2
>> > > 
>> > > This is shown in dpll device GET/DUMP outputs.
>> > > Also user passes it during SET operation:
>> > > $ dplltool set ice/c92d02a7129f4747/1 mode auto
>> > > 
>> > > Isn't that enough stable and nice?
>> > > 
>> > 
>> > I agree with Vadim, this is rather to be used by a daemon tools, which
>> > would get the index once, then could use it as long as device is there.
>> 
>> So basically you say, you can have 2 approaches in app:
>> 1)
>> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> 2):
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> 
>> What is exactly benefit of the first one? Why to have 2 handles? Devlink
>> is a nice example of 2) approach, no problem there.
>> 
>> Perhaps I'm missing something, but looks like you want the id for no
>> good reason and this dual-handle scheme just makes things more
>> complicated with 0 added value.
>
>I would like to avoid any extra memory copies or memory checks when it's
>possible to compare single u32/u64 index value. I might be invisible on a
>single host setup, but running monitoring at scale which will parse and
>compare string on every get/event can burn a bit of compute capacity.

Wait, that does not make any sense what so ever.
Show me numbers and real usecase. A sane app gets once at start, then
processes notifications. You have a flow where string compare on a
netlink command makes difference to int compare? Like tens of thousands
of netlink cmds per second? I doubt that. If yes, it is a misuse. Btw,
the string compare would be your last problem comparing the overhead of
Netlink processing with ioctl for example.

Your dual handle scheme just adds complexicity, confusion with 0 added
value. Please drop it. I really don't understand the need to defend this
odd approach :/

[...]


>> > > > > > +
>> > > > > > +	return dpll_pre_dumpit(cb);
>> > > > > > +}
>> > > > > > +
>> > > > > > +int dpll_pin_post_dumpit(struct netlink_callback *cb)
>> > > > > > +{
>> > > > > > +	mutex_unlock(&dpll_pin_xa_lock);
>> > > > > > +
>> > > > > > +	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-
>> > > > > > dev_driver_id))
>> > > > > > +		return -EMSGSIZE;
>> > > > > 
>> > > > > I don't really understand why you are trying figure something new and
>> > > > > interesting with the change notifications. This object mix and random
>> > > > > attrs fillup is something very wrong and makes userspace completely
>> > > > > fuzzy about what it is getting. And yet it is so simple:
>> > > > > You have 2 objects, dpll and pin, please just have:
>> > > > > dpll_notify()
>> > > > > dpll_pin_notify()
>> > > > > and share the attrs fillup code with pin_get() and dpll_get() callbacks.
>> > > > > No need for any smartness. Have this dumb and simple.
>> > > > > 
>> > > > > Think about it more as about "object-state-snapshot" than "atomic-change"
>> > > > 
>> > > > But with full object-snapshot user space app will lose the information about
>> > > > what exactly has changed. The reason to have this event is to provide the
>> > > > attributes which have changed. Otherwise, the app should have full snapshot
>> > > > and
>> > > > compare all attributes to figure out changes and that's might not be great
>> > > > idea.
>> > > 
>> > > Wait, are you saying that the app is stateless? Could you provide
>> > > example use cases?
>> > > 
>> > >From what I see, the app managing dpll knows the state of the device and
>> > > pins, it monitors for the changes and saves new state with appropriate
>> > > reaction (might be some action or maybe just log entry).
>> > > 
>> > 
>> > It depends on the use case, right? App developer having those information knows
>> > what has changed, thus can react in a way it thinks is most suitable.
>> > IMHO from user perspective it is good to have a notification which actually
>> > shows it's reason, so proper flow could be assigned to handle the reaction.
>> 
>> Again, could you provide me specific example in which this is needed?
>> I may be missing something, but I don't see how it can bring
>> and benefit. It just makes the live of the app harder because it has to
>> treat the get and notify messages differently.
>> 
>> It is quite common for app to:
>> init:
>> 1) get object state
>> 2) store it
>> 3) apply configuration
>> runtime:
>> 1) listen to object state change
>> 2) store it
>> 3) apply configuration
>> 
>> Same code for both.
>
>Well, I'm thinking about simple monitoring app which will wait for the events
>and create an alert if the changes coming from the event differ from the
>"allowed configs". In this case no real reason to store whole object state,
>but the changes in events are very useful.

No problem. But as I wrote elsewhere in this thread, make sure that msg
format of a change message is the same as the format of get cmd message.
Basically think of it as a get cmd message filtered to have only
attrs which changed. Same nesting and everything. Makes it simple for
the app to have same parsing code for get/dump/notification messages.

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
  2023-03-31 23:28           ` Vadim Fedorenko
@ 2023-04-01 12:53             ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-04-01 12:53 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sat, Apr 01, 2023 at 01:28:29AM CEST, vadim.fedorenko@linux.dev wrote:
>On 15.03.2023 12:24, Jiri Pirko wrote:
>> Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>> > On 14/03/2023 10:05, Jiri Pirko wrote:
>> > > Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]


>> > > > +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> > > > +{
>> > > > +	int i;
>> > > > +
>> > > > +	for (i = 0; i < 4; i++) {
>> > > > +		if (bp->sma[i].dpll_pin == pin)
>> > > > +			return i;
>> > > 
>> > > Just pass &bp->sma[i] as a priv to dpll_pin_register().
>> > > and get that pointer directly in pin ops. No need for lookup and use of
>> > > sma_nr at all.
>> > 
>> > I'm still thinking about the change that you proposed to remove these
>> > _priv() helpers. I have to look into ice code to be sure we won't break
>> > any assumptions in it with moving to additional (void *) parameter.
>> 
>> There are basically 2 ways:
>> someop(struct subsystemobj *x, void *priv)
>> {
>> 	struct *mine = priv;
>> }
>> or:
>> someop(struct subsystemobj *x)
>> {
>> 	struct *mine = subsystem_get_priv(x);
>> }
>> 
>> Both are more or less equal. The first has benefit that the caller most
>> usually has direct access to the priv, so it can just easily pass it on.
>> Also, you as the driver write see right away there is a priv arg and
>> makes you want to use it and not figure out odd lookups to get to the
>> same priv.
>
>Thinking about this part. We have tons of parameters for some ops already.
>Adding void *priv to every one of them (and actually two for pins) makes them
>even more ugly. Let's stay with helpers if you don't have strong opinion
>against.

Well, with the patches I sent when 1 device/pin can be registered
multiple times with multiple privs, I believe it is much more feasable
to just pass the priv along in the arg list, because otherwise you would
have to do some odd lookup.

Please pass priv, would be nicer and easier and more simple for the
driver to implement. The arg list can handle that easily.

[...]

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

* Re: [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops
@ 2023-04-01 12:53             ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-04-01 12:53 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Vadim Fedorenko, Jakub Kicinski, Arkadiusz Kubalewski,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk

Sat, Apr 01, 2023 at 01:28:29AM CEST, vadim.fedorenko@linux.dev wrote:
>On 15.03.2023 12:24, Jiri Pirko wrote:
>> Wed, Mar 15, 2023 at 01:10:33AM CET, vadim.fedorenko@linux.dev wrote:
>> > On 14/03/2023 10:05, Jiri Pirko wrote:
>> > > Sun, Mar 12, 2023 at 03:28:07AM CET, vadfed@meta.com wrote:

[...]


>> > > > +static int ptp_ocp_dpll_pin_to_sma(const struct ptp_ocp *bp, const struct dpll_pin *pin)
>> > > > +{
>> > > > +	int i;
>> > > > +
>> > > > +	for (i = 0; i < 4; i++) {
>> > > > +		if (bp->sma[i].dpll_pin == pin)
>> > > > +			return i;
>> > > 
>> > > Just pass &bp->sma[i] as a priv to dpll_pin_register().
>> > > and get that pointer directly in pin ops. No need for lookup and use of
>> > > sma_nr at all.
>> > 
>> > I'm still thinking about the change that you proposed to remove these
>> > _priv() helpers. I have to look into ice code to be sure we won't break
>> > any assumptions in it with moving to additional (void *) parameter.
>> 
>> There are basically 2 ways:
>> someop(struct subsystemobj *x, void *priv)
>> {
>> 	struct *mine = priv;
>> }
>> or:
>> someop(struct subsystemobj *x)
>> {
>> 	struct *mine = subsystem_get_priv(x);
>> }
>> 
>> Both are more or less equal. The first has benefit that the caller most
>> usually has direct access to the priv, so it can just easily pass it on.
>> Also, you as the driver write see right away there is a priv arg and
>> makes you want to use it and not figure out odd lookups to get to the
>> same priv.
>
>Thinking about this part. We have tons of parameters for some ops already.
>Adding void *priv to every one of them (and actually two for pins) makes them
>even more ugly. Let's stay with helpers if you don't have strong opinion
>against.

Well, with the patches I sent when 1 device/pin can be registered
multiple times with multiple privs, I believe it is much more feasable
to just pass the priv along in the arg list, because otherwise you would
have to do some odd lookup.

Please pass priv, would be nicer and easier and more simple for the
driver to implement. The arg list can handle that easily.

[...]

_______________________________________________
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] 202+ messages in thread

* Re: [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
  2023-03-28 16:36     ` Vadim Fedorenko
@ 2023-04-01 12:54       ` Jiri Pirko
  -1 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-04-01 12:54 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: netdev, arkadiusz.kubalewski, vadfed, kuba, jonathan.lemon,
	pabeni, poros, mschmidt, linux-arm-kernel, linux-clk

Tue, Mar 28, 2023 at 06:36:13PM CEST, vadim.fedorenko@linux.dev wrote:
>On 26/03/2023 18:00, Jiri Pirko wrote:
>> From: Jiri Pirko <jiri@nvidia.com>
>> 
>> Hi.
>> 
>> This is extending your patchset. Basically, I do this on top of the
>> changes I pointed out during review. For example patch #6 is exposing
>> pin handle which is going to change, etc (there, I put a note).
>> 
>> First 5 patches are just needed dependencies and you can squash them
>> into your patch/patches. Last two patches should go in separatelly.
>> 
>> Please note that the patch #6 is replacing the need to pass the rclk
>> device during pin registration by putting a link between netdev and dpll
>> pin.
>> 
>> Please merge this into your dpll patchset and include it in the next
>> RFC. Thanks!
>> 
>
>Hi Jiri!
>
>Thanks for the patch set. It looks like it covers some changes that I have
>also done to address the comments. I'll try to combine everything in a couple
>of days and will re-spin series and we restart review process. I think that
>there are open question still in the conversation which were not answered.

I hope I didn't miss anything. I believe I replied to every comment.
Please tell me if not.

Thanks!


>
>Best,
>Vadim

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

* Re: [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation
@ 2023-04-01 12:54       ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-04-01 12:54 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: netdev, arkadiusz.kubalewski, vadfed, kuba, jonathan.lemon,
	pabeni, poros, mschmidt, linux-arm-kernel, linux-clk

Tue, Mar 28, 2023 at 06:36:13PM CEST, vadim.fedorenko@linux.dev wrote:
>On 26/03/2023 18:00, Jiri Pirko wrote:
>> From: Jiri Pirko <jiri@nvidia.com>
>> 
>> Hi.
>> 
>> This is extending your patchset. Basically, I do this on top of the
>> changes I pointed out during review. For example patch #6 is exposing
>> pin handle which is going to change, etc (there, I put a note).
>> 
>> First 5 patches are just needed dependencies and you can squash them
>> into your patch/patches. Last two patches should go in separatelly.
>> 
>> Please note that the patch #6 is replacing the need to pass the rclk
>> device during pin registration by putting a link between netdev and dpll
>> pin.
>> 
>> Please merge this into your dpll patchset and include it in the next
>> RFC. Thanks!
>> 
>
>Hi Jiri!
>
>Thanks for the patch set. It looks like it covers some changes that I have
>also done to address the comments. I'll try to combine everything in a couple
>of days and will re-spin series and we restart review process. I think that
>there are open question still in the conversation which were not answered.

I hope I didn't miss anything. I believe I replied to every comment.
Please tell me if not.

Thanks!


>
>Best,
>Vadim

_______________________________________________
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] 202+ messages in thread

* RE: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
  2023-03-14 16:14     ` Jiri Pirko
@ 2023-04-03 10:21       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-04-03 10:21 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 5:15 PM
>
>Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>>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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
>> Documentation/networking/index.rst |   1 +
>> 2 files changed, 348 insertions(+)
>> create mode 100644 Documentation/networking/dpll.rst
>>
>>diff --git a/Documentation/networking/dpll.rst
>>b/Documentation/networking/dpll.rst
>>new file mode 100644
>>index 000000000000..25cd81edc73c
>>--- /dev/null
>>+++ b/Documentation/networking/dpll.rst
>>@@ -0,0 +1,347 @@
>>+.. 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.
>>+
>>+
>>+Dpll object
>
>rather perhaps "Device object"?
>

Sure, fixed.

>
>>+===========
>>+Single dpll device object means single Digital PLL circuit and bunch of
>>+pins connected with it.
>>+It provides its capablities and current status to the user in response
>
>Which capabilities you have in mind. There is CAPs for pins, but I see
>none for device.
>

Ok, changed "capabilities" to "supported working modes".

>
>>+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 input or output, it
>
>In the code and UAPI you use terms "source" and "output". Please align
>to that.
>

Sure, fixed.

>
>>+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 properities and capabilities are provided to the user in response
>
>s/properities/properties/
>

Thanks, fixed.

>There is more provided, like "status" for example.
>

Makes sense, fixed.

>
>>+to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
>>+It is also possible to list all the pins that were registered either
>>+with dpll or different pin with `dump` request of ``DPLL_CMD_PIN_GET``
>
>I don't follow. Dump of DPLL_CMD_PIN_GET just dumps always all the pins
>in the system. Am I missing something?
>

Yes you are right, rephrased to make it clearer.

>
>>+command.
>>+Configuration of a pin can be changed by `do` request of netlink
>>+``DPLL_CMD_PIN_SET`` command.
>>+
>>+
>>+Shared pins
>>+===========
>>+Pin can be shared by multiple dpll devices. Where configuration on one
>>+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
>>+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).
>
>Perhaps this can be extended to something like this:
>
>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).
>
>

Makes sense, fixed.

>>+
>>+
>>+MUX-type pins
>>+=============
>>+A pin can be MUX-type, which aggregates child pins and serves as pin
>>+multiplexer. One or more pins are attached to MUX-type instead of being
>>+directly connected to a dpll device.
>
>Perhaps you can say "registered" in stead of "connected" and "attached"
>so this is aligned with the function names?
>

Makes sense, fixed.

>
>>+Pins registered with a MUX-type provides user with additional nested
>
>s/provides/provide/
>

Sure, fixed.
 
>
>>+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered
>
>s/parrent/parent/
>
>I'm confused. Can one pin be registered to multiple parents? How is that
>supposed to be working?
>


As we have agreed, MUXed pins can have multiple parents.
In our case:
/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--do pin-get --json '{"id": 0, "pin-idx":13}'
{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
'id': 0},
                     {'bus-name': 'pci',
                      'dev-name': '0000:21:00.0_1',
                      'id': 1}],
          'pin-description': '0000:21:00.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'}}],
          'pin-rclk-device': '0000:21:00.0',
          'pin-type': {'doc': "ethernet port PHY's recovered clock",
                       'name': 'synce-eth-port'}}]}


I will add some more information and the example.

>
>>+with.
>>+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.
>
>Tell the enum value.
>

Sure, fixed.

>
>>+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 are not capable of automatic source pin selection,
>
>s/are not/is not/
>

Thanks, fixed.

>
>>+in order to configure a source of a MUX-type pin the user still needs
>>+to request desired pin state.
>
>Perhaps emphasize that this is the state of the child?
>

Makes sense, fixed.

>
>>+
>>+
>>+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 command 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
>>+    ``SOURCE_PIN_IDX``          attr index of currently selected source
>>+    ``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
>
>Ah, now I understand what this is about. Didn't occur to me from the
>netlink UAPI :/
>

It's like assembling furniture without the manual :)

>
>>+      ``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_PRIO``                attr pin priority to be set
>
>I think it would be good to emhasize which attribute is valid in which
>combination, meaning alone, dpll needs to be specified, parent pin needs
>to be specified
>

I agree, we need to do something like it.

>
>>+    ``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. Possible response message attributes for netlink dump
>>+requests:
>>+
>>+  ==============================  =======================================
>>+  ``PIN_GET``                     command to dump pins
>
>Maintain the order and start with DEVICE_GET
>

Sure, fixed.

>
>
>>+    ``PIN``                       attr nested type contains single pin
>>+      ``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_IDX``                 attr index of dumped pin (on dplls)
>>+      ``PIN_DESCRIPTION``         description of a pin provided by driver
>>+      ``PIN_TYPE``                attr value of pin type
>>+      ``PIN_FREQUENCY``           attr current frequency of a pin
>>+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
>>+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
>>+                                  recovers clock signal from
>>+      ``PIN_DIRECTION``           attr direction of a pin
>>+      ``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
>>+
>>+  ``DEVICE_GET``                  command to dump dplls
>>+    ``DEVICE``                    attr nested type contatin a single
>>+                                  dpll device
>>+      ``ID``                      attr internal dpll device ID
>>+      ``DEV_NAME``                attr dpll device name
>>+      ``BUS_NAME``                attr dpll device bus name
>
>Hmm, why you need to repeat this for dump? Just say the message format
>is the same as for DO and you are done with it.
>

Yeah. Fixed.

>
>>+
>>+
>>+Dpll 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 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 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 used to provide pulse-per-second output
>>+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
>>+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
>>+
>>+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 can register a dpll device within dpll subsytem.
>
>"Driver instance". Btw, I need to change it for mlx5. I will try to
>implement it in coming days.
>

Sure, fixed.

>
>>+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
>
>In this text, you use "dpll", "Dpll", "DPLL". Could you unify?
>

Sure, fixed.

>
>
>>+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 id) use ``dpll_device_get``
>
>s/to id/to it/
>

Thanks, fixed.

>
>>+to obtain proper dpll device pointer.
>>+
>>+The name od DPLL device is generated based on registerer provided device
>
>s/name od/name of/

Thanks, fixed.

>
>
>>+struct pointer and dev_driver_id value.
>>+Name is in format: ``%s_%u`` witch arguments:
>>+``dev_name(struct device *)`` - syscall on parent device struct
>>+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
>>+driver with ``dpll_device_notify`` corresponding attribute as a reason.
>>+
>>+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.
>
>Not sure internal implementation details are necessary. Just say that
>driver is free to implement a set of ops it supports, leave the rest not
>implemented.
>

Hmm, actually this sentence is not true.
Some commands are required - as we WARN about them...
I will list them there.


Thank you,
Arkadiusz
>
>>+
>>diff --git a/Documentation/networking/index.rst
>b/Documentation/networking/index.rst
>>index 4ddcae33c336..6eb83a47cc2d 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	[flat|nested] 202+ messages in thread

* RE: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
@ 2023-04-03 10:21       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-04-03 10:21 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, March 14, 2023 5:15 PM
>
>Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>>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/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
>> Documentation/networking/index.rst |   1 +
>> 2 files changed, 348 insertions(+)
>> create mode 100644 Documentation/networking/dpll.rst
>>
>>diff --git a/Documentation/networking/dpll.rst
>>b/Documentation/networking/dpll.rst
>>new file mode 100644
>>index 000000000000..25cd81edc73c
>>--- /dev/null
>>+++ b/Documentation/networking/dpll.rst
>>@@ -0,0 +1,347 @@
>>+.. 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.
>>+
>>+
>>+Dpll object
>
>rather perhaps "Device object"?
>

Sure, fixed.

>
>>+===========
>>+Single dpll device object means single Digital PLL circuit and bunch of
>>+pins connected with it.
>>+It provides its capablities and current status to the user in response
>
>Which capabilities you have in mind. There is CAPs for pins, but I see
>none for device.
>

Ok, changed "capabilities" to "supported working modes".

>
>>+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 input or output, it
>
>In the code and UAPI you use terms "source" and "output". Please align
>to that.
>

Sure, fixed.

>
>>+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 properities and capabilities are provided to the user in response
>
>s/properities/properties/
>

Thanks, fixed.

>There is more provided, like "status" for example.
>

Makes sense, fixed.

>
>>+to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
>>+It is also possible to list all the pins that were registered either
>>+with dpll or different pin with `dump` request of ``DPLL_CMD_PIN_GET``
>
>I don't follow. Dump of DPLL_CMD_PIN_GET just dumps always all the pins
>in the system. Am I missing something?
>

Yes you are right, rephrased to make it clearer.

>
>>+command.
>>+Configuration of a pin can be changed by `do` request of netlink
>>+``DPLL_CMD_PIN_SET`` command.
>>+
>>+
>>+Shared pins
>>+===========
>>+Pin can be shared by multiple dpll devices. Where configuration on one
>>+pin can alter multiple dplls (i.e. PIN_FREQUENCY, PIN_DIRECTION),
>>+or configure just one pin-dpll pair (i.e. PIN_PRIO, PIN_STATE).
>
>Perhaps this can be extended to something like this:
>
>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).
>
>

Makes sense, fixed.

>>+
>>+
>>+MUX-type pins
>>+=============
>>+A pin can be MUX-type, which aggregates child pins and serves as pin
>>+multiplexer. One or more pins are attached to MUX-type instead of being
>>+directly connected to a dpll device.
>
>Perhaps you can say "registered" in stead of "connected" and "attached"
>so this is aligned with the function names?
>

Makes sense, fixed.

>
>>+Pins registered with a MUX-type provides user with additional nested
>
>s/provides/provide/
>

Sure, fixed.
 
>
>>+attribute ``DPLL_A_PIN_PARENT`` for each parrent they were registered
>
>s/parrent/parent/
>
>I'm confused. Can one pin be registered to multiple parents? How is that
>supposed to be working?
>


As we have agreed, MUXed pins can have multiple parents.
In our case:
/tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml 
--do pin-get --json '{"id": 0, "pin-idx":13}'
{'pin': [{'device': [{'bus-name': 'pci', 'dev-name': '0000:21:00.0_0',
'id': 0},
                     {'bus-name': 'pci',
                      'dev-name': '0000:21:00.0_1',
                      'id': 1}],
          'pin-description': '0000:21:00.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'}}],
          'pin-rclk-device': '0000:21:00.0',
          'pin-type': {'doc': "ethernet port PHY's recovered clock",
                       'name': 'synce-eth-port'}}]}


I will add some more information and the example.

>
>>+with.
>>+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.
>
>Tell the enum value.
>

Sure, fixed.

>
>>+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 are not capable of automatic source pin selection,
>
>s/are not/is not/
>

Thanks, fixed.

>
>>+in order to configure a source of a MUX-type pin the user still needs
>>+to request desired pin state.
>
>Perhaps emphasize that this is the state of the child?
>

Makes sense, fixed.

>
>>+
>>+
>>+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 command 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
>>+    ``SOURCE_PIN_IDX``          attr index of currently selected source
>>+    ``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
>
>Ah, now I understand what this is about. Didn't occur to me from the
>netlink UAPI :/
>

It's like assembling furniture without the manual :)

>
>>+      ``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_PRIO``                attr pin priority to be set
>
>I think it would be good to emhasize which attribute is valid in which
>combination, meaning alone, dpll needs to be specified, parent pin needs
>to be specified
>

I agree, we need to do something like it.

>
>>+    ``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. Possible response message attributes for netlink dump
>>+requests:
>>+
>>+  ==============================  =======================================
>>+  ``PIN_GET``                     command to dump pins
>
>Maintain the order and start with DEVICE_GET
>

Sure, fixed.

>
>
>>+    ``PIN``                       attr nested type contains single pin
>>+      ``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_IDX``                 attr index of dumped pin (on dplls)
>>+      ``PIN_DESCRIPTION``         description of a pin provided by driver
>>+      ``PIN_TYPE``                attr value of pin type
>>+      ``PIN_FREQUENCY``           attr current frequency of a pin
>>+      ``PIN_FREQUENCY_SUPPORTED`` attr provides supported frequencies
>>+      ``PIN_RCLK_DEVICE``         attr name of a device, where pin
>>+                                  recovers clock signal from
>>+      ``PIN_DIRECTION``           attr direction of a pin
>>+      ``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
>>+
>>+  ``DEVICE_GET``                  command to dump dplls
>>+    ``DEVICE``                    attr nested type contatin a single
>>+                                  dpll device
>>+      ``ID``                      attr internal dpll device ID
>>+      ``DEV_NAME``                attr dpll device name
>>+      ``BUS_NAME``                attr dpll device bus name
>
>Hmm, why you need to repeat this for dump? Just say the message format
>is the same as for DO and you are done with it.
>

Yeah. Fixed.

>
>>+
>>+
>>+Dpll 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 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 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 used to provide pulse-per-second output
>>+  ``TYPE_EEC``  DPLL 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 connected to a dpll or parent pin
>>+  ``STATE_DISCONNECTED``  Pin disconnected from dpll or parent pin
>>+
>>+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 can register a dpll device within dpll subsytem.
>
>"Driver instance". Btw, I need to change it for mlx5. I will try to
>implement it in coming days.
>

Sure, fixed.

>
>>+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
>
>In this text, you use "dpll", "Dpll", "DPLL". Could you unify?
>

Sure, fixed.

>
>
>>+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 id) use ``dpll_device_get``
>
>s/to id/to it/
>

Thanks, fixed.

>
>>+to obtain proper dpll device pointer.
>>+
>>+The name od DPLL device is generated based on registerer provided device
>
>s/name od/name of/

Thanks, fixed.

>
>
>>+struct pointer and dev_driver_id value.
>>+Name is in format: ``%s_%u`` witch arguments:
>>+``dev_name(struct device *)`` - syscall on parent device struct
>>+``dev_driver_idx``            - registerer given 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 dpll status changes shall be requested by device
>>+driver with ``dpll_device_notify`` corresponding attribute as a reason.
>>+
>>+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.
>
>Not sure internal implementation details are necessary. Just say that
>driver is free to implement a set of ops it supports, leave the rest not
>implemented.
>

Hmm, actually this sentence is not true.
Some commands are required - as we WARN about them...
I will list them there.


Thank you,
Arkadiusz
>
>>+
>>diff --git a/Documentation/networking/index.rst
>b/Documentation/networking/index.rst
>>index 4ddcae33c336..6eb83a47cc2d 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	[flat|nested] 202+ messages in thread

* RE: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
  2023-03-16 13:46     ` Jiri Pirko
@ 2023-04-03 10:23       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-04-03 10:23 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 2:46 PM
>
>Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>> Documentation/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
>
>Why this is under networking? It is not networking. Please move, probably
>directly under "Documentation"
>
>[...]

Fixed, now in Documentation/dpll.rst

Thank you,
Arkadiusz

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

* RE: [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface
@ 2023-04-03 10:23       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 202+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-04-03 10:23 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Vadim Fedorenko,
	poros, mschmidt, netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, March 16, 2023 2:46 PM
>
>Sun, Mar 12, 2023 at 03:28:04AM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>> Documentation/networking/dpll.rst  | 347 +++++++++++++++++++++++++++++
>
>Why this is under networking? It is not networking. Please move, probably
>directly under "Documentation"
>
>[...]

Fixed, now in Documentation/dpll.rst

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] 202+ messages in thread

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

On Wed, 15 Mar 2023 10:22:33 +0100 Jiri Pirko wrote:
> So basically you say, you can have 2 approaches in app:
> 1)
> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> 2):
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> 
> What is exactly benefit of the first one? Why to have 2 handles? Devlink
> is a nice example of 2) approach, no problem there.

IMHO for devlink the neatness of using the name came from the fact 
that the device name was meaningful. 

With the advent of auxbus that's no longer the case.

In fact it seems more than likely that changing the name to auxbus
will break FW update scripts. Maybe nobody has complained yet only
because prod adoption of these APIs is generally lacking :(

I agree that supporting both name and ID is pointless, user space can
translate between the two trivially all by itself. But I'd lean towards
deleting the name support not the ID support :(

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

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

On Wed, 15 Mar 2023 10:22:33 +0100 Jiri Pirko wrote:
> So basically you say, you can have 2 approaches in app:
> 1)
> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> dpll_device_set(id, something);
> 2):
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> dpll_device_set("ice/c92d02a7129f4747/1, something);
> 
> What is exactly benefit of the first one? Why to have 2 handles? Devlink
> is a nice example of 2) approach, no problem there.

IMHO for devlink the neatness of using the name came from the fact 
that the device name was meaningful. 

With the advent of auxbus that's no longer the case.

In fact it seems more than likely that changing the name to auxbus
will break FW update scripts. Maybe nobody has complained yet only
because prod adoption of these APIs is generally lacking :(

I agree that supporting both name and ID is pointless, user space can
translate between the two trivially all by itself. But I'd lean towards
deleting the name support not the ID support :(

_______________________________________________
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] 202+ messages in thread

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

Mon, Apr 03, 2023 at 08:18:12PM CEST, kuba@kernel.org wrote:
>On Wed, 15 Mar 2023 10:22:33 +0100 Jiri Pirko wrote:
>> So basically you say, you can have 2 approaches in app:
>> 1)
>> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> 2):
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> 
>> What is exactly benefit of the first one? Why to have 2 handles? Devlink
>> is a nice example of 2) approach, no problem there.
>
>IMHO for devlink the neatness of using the name came from the fact 
>that the device name was meaningful. 
>
>With the advent of auxbus that's no longer the case.
>
>In fact it seems more than likely that changing the name to auxbus
>will break FW update scripts. Maybe nobody has complained yet only
>because prod adoption of these APIs is generally lacking :(
>
>I agree that supporting both name and ID is pointless, user space can
>translate between the two trivially all by itself. But I'd lean towards
>deleting the name support not the ID support :(

Wait, not sure you get the format of the "name". It does not contain any
bus address, so the auxdev issue you pointed out is not applicable.
It is driver/clock_id/index.
All 3 are stable and user can rely on them. Do you see any issue in
that?

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

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

Mon, Apr 03, 2023 at 08:18:12PM CEST, kuba@kernel.org wrote:
>On Wed, 15 Mar 2023 10:22:33 +0100 Jiri Pirko wrote:
>> So basically you say, you can have 2 approaches in app:
>> 1)
>> id = dpll_device_get_id("ice/c92d02a7129f4747/1")
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> dpll_device_set(id, something);
>> 2):
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> dpll_device_set("ice/c92d02a7129f4747/1, something);
>> 
>> What is exactly benefit of the first one? Why to have 2 handles? Devlink
>> is a nice example of 2) approach, no problem there.
>
>IMHO for devlink the neatness of using the name came from the fact 
>that the device name was meaningful. 
>
>With the advent of auxbus that's no longer the case.
>
>In fact it seems more than likely that changing the name to auxbus
>will break FW update scripts. Maybe nobody has complained yet only
>because prod adoption of these APIs is generally lacking :(
>
>I agree that supporting both name and ID is pointless, user space can
>translate between the two trivially all by itself. But I'd lean towards
>deleting the name support not the ID support :(

Wait, not sure you get the format of the "name". It does not contain any
bus address, so the auxdev issue you pointed out is not applicable.
It is driver/clock_id/index.
All 3 are stable and user can rely on them. Do you see any issue in
that?

_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
       [not found]                 ` <20230410153149.602c6bad@kernel.org>
@ 2023-04-16 16:23                     ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-04-16 16:23 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Vadim Fedorenko,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

Tue, Apr 11, 2023 at 12:31:49AM CEST, kuba@kernel.org wrote:
>On Sun, 9 Apr 2023 09:51:48 +0200 Jiri Pirko wrote:
>> Wait, not sure you get the format of the "name". It does not contain any
>> bus address, so the auxdev issue you pointed out is not applicable.
>> It is driver/clock_id/index.
>> All 3 are stable and user can rely on them. Do you see any issue in
>> that?
>
>What is index? I thought you don't want an index and yet there is one,
>just scoped by random attributes :(

Index internal within a single instance. Like Intel guys, they have 1
clock wired up with multiple DPLLs. The driver gives every DPLL index.
This is internal, totally up to the driver decision. Similar concept to
devlink port index.


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

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

Tue, Apr 11, 2023 at 12:31:49AM CEST, kuba@kernel.org wrote:
>On Sun, 9 Apr 2023 09:51:48 +0200 Jiri Pirko wrote:
>> Wait, not sure you get the format of the "name". It does not contain any
>> bus address, so the auxdev issue you pointed out is not applicable.
>> It is driver/clock_id/index.
>> All 3 are stable and user can rely on them. Do you see any issue in
>> that?
>
>What is index? I thought you don't want an index and yet there is one,
>just scoped by random attributes :(

Index internal within a single instance. Like Intel guys, they have 1
clock wired up with multiple DPLLs. The driver gives every DPLL index.
This is internal, totally up to the driver decision. Similar concept to
devlink port index.


_______________________________________________
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] 202+ messages in thread

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

On 16/04/2023 17:23, Jiri Pirko wrote:
> Tue, Apr 11, 2023 at 12:31:49AM CEST, kuba@kernel.org wrote:
>> On Sun, 9 Apr 2023 09:51:48 +0200 Jiri Pirko wrote:
>>> Wait, not sure you get the format of the "name". It does not contain any
>>> bus address, so the auxdev issue you pointed out is not applicable.
>>> It is driver/clock_id/index.
>>> All 3 are stable and user can rely on them. Do you see any issue in
>>> that?
>>
>> What is index? I thought you don't want an index and yet there is one,
>> just scoped by random attributes :(
> 
> Index internal within a single instance. Like Intel guys, they have 1
> clock wired up with multiple DPLLs. The driver gives every DPLL index.
> This is internal, totally up to the driver decision. Similar concept to
> devlink port index.

It feels like a dead-lock in conversation here. We have to agree on 
something because for now it's the only blocker to post the next version 
with all the comments from the previous one addressed in the code.
My position here is that I'm ok to have any of the properties being an 
identifier as well as keep both of them, the code already has all the 
lines to support any decision. I just to want to go back to this part 
again in the next iteration.


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

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

On 16/04/2023 17:23, Jiri Pirko wrote:
> Tue, Apr 11, 2023 at 12:31:49AM CEST, kuba@kernel.org wrote:
>> On Sun, 9 Apr 2023 09:51:48 +0200 Jiri Pirko wrote:
>>> Wait, not sure you get the format of the "name". It does not contain any
>>> bus address, so the auxdev issue you pointed out is not applicable.
>>> It is driver/clock_id/index.
>>> All 3 are stable and user can rely on them. Do you see any issue in
>>> that?
>>
>> What is index? I thought you don't want an index and yet there is one,
>> just scoped by random attributes :(
> 
> Index internal within a single instance. Like Intel guys, they have 1
> clock wired up with multiple DPLLs. The driver gives every DPLL index.
> This is internal, totally up to the driver decision. Similar concept to
> devlink port index.

It feels like a dead-lock in conversation here. We have to agree on 
something because for now it's the only blocker to post the next version 
with all the comments from the previous one addressed in the code.
My position here is that I'm ok to have any of the properties being an 
identifier as well as keep both of them, the code already has all the 
lines to support any decision. I just to want to go back to this part 
again in the next iteration.


_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
       [not found]                     ` <20230417124942.4305abfa@kernel.org>
@ 2023-04-27  8:05                         ` Paolo Abeni
       [not found]                       ` <ZFDPaXlJainSOqmV@nanopsycho>
  1 sibling, 0 replies; 202+ messages in thread
From: Paolo Abeni @ 2023-04-27  8:05 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Vadim Fedorenko,
	Jonathan Lemon, poros, mschmidt, netdev, linux-arm-kernel

Hi,

On Mon, 2023-04-17 at 12:49 -0700, Jakub Kicinski wrote:
> [resend with fixed CC list]
> 
> On Sun, 16 Apr 2023 18:23:15 +0200 Jiri Pirko wrote:
> > > What is index? I thought you don't want an index and yet there is one,
> > > just scoped by random attributes :(  
> > 
> > Index internal within a single instance. Like Intel guys, they have 1
> > clock wired up with multiple DPLLs. The driver gives every DPLL index.
> > This is internal, totally up to the driver decision. Similar concept to
> > devlink port index.
> 
> devlink port index ended up as a pretty odd beast with drivers encoding
> various information into it, using locally grown schemes.
> 
> Hard no on doing that in dpll, it should not be exposed to the user.

I'm replying here just in case the even the above reply was lost.

I guess the last remark resolved this specific discussion.

@Vadim, @Arkadiusz, even if net-next is currently closed, there are no
restrictions for RFC patches: feel free to share v7 when it fits you
better!

Thanks,

Paolo


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

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

Hi,

On Mon, 2023-04-17 at 12:49 -0700, Jakub Kicinski wrote:
> [resend with fixed CC list]
> 
> On Sun, 16 Apr 2023 18:23:15 +0200 Jiri Pirko wrote:
> > > What is index? I thought you don't want an index and yet there is one,
> > > just scoped by random attributes :(  
> > 
> > Index internal within a single instance. Like Intel guys, they have 1
> > clock wired up with multiple DPLLs. The driver gives every DPLL index.
> > This is internal, totally up to the driver decision. Similar concept to
> > devlink port index.
> 
> devlink port index ended up as a pretty odd beast with drivers encoding
> various information into it, using locally grown schemes.
> 
> Hard no on doing that in dpll, it should not be exposed to the user.

I'm replying here just in case the even the above reply was lost.

I guess the last remark resolved this specific discussion.

@Vadim, @Arkadiusz, even if net-next is currently closed, there are no
restrictions for RFC patches: feel free to share v7 when it fits you
better!

Thanks,

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] 202+ messages in thread

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

On 27/04/2023 09:05, Paolo Abeni wrote:
> Hi,
> 
> On Mon, 2023-04-17 at 12:49 -0700, Jakub Kicinski wrote:
>> [resend with fixed CC list]
>>
>> On Sun, 16 Apr 2023 18:23:15 +0200 Jiri Pirko wrote:
>>>> What is index? I thought you don't want an index and yet there is one,
>>>> just scoped by random attributes :(
>>>
>>> Index internal within a single instance. Like Intel guys, they have 1
>>> clock wired up with multiple DPLLs. The driver gives every DPLL index.
>>> This is internal, totally up to the driver decision. Similar concept to
>>> devlink port index.
>>
>> devlink port index ended up as a pretty odd beast with drivers encoding
>> various information into it, using locally grown schemes.
>>
>> Hard no on doing that in dpll, it should not be exposed to the user.
> 
> I'm replying here just in case the even the above reply was lost.
> 
> I guess the last remark resolved this specific discussion.
> 
> @Vadim, @Arkadiusz, even if net-next is currently closed, there are no
> restrictions for RFC patches: feel free to share v7 when it fits you
> better!

Hi Paolo!
Thanks for reminder, the new version is almost ready, I was a bit 
distracted by other burning issues. Hopefully I'll publish it till the 
end of the week.


_______________________________________________
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] 202+ messages in thread

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

On 27/04/2023 09:05, Paolo Abeni wrote:
> Hi,
> 
> On Mon, 2023-04-17 at 12:49 -0700, Jakub Kicinski wrote:
>> [resend with fixed CC list]
>>
>> On Sun, 16 Apr 2023 18:23:15 +0200 Jiri Pirko wrote:
>>>> What is index? I thought you don't want an index and yet there is one,
>>>> just scoped by random attributes :(
>>>
>>> Index internal within a single instance. Like Intel guys, they have 1
>>> clock wired up with multiple DPLLs. The driver gives every DPLL index.
>>> This is internal, totally up to the driver decision. Similar concept to
>>> devlink port index.
>>
>> devlink port index ended up as a pretty odd beast with drivers encoding
>> various information into it, using locally grown schemes.
>>
>> Hard no on doing that in dpll, it should not be exposed to the user.
> 
> I'm replying here just in case the even the above reply was lost.
> 
> I guess the last remark resolved this specific discussion.
> 
> @Vadim, @Arkadiusz, even if net-next is currently closed, there are no
> restrictions for RFC patches: feel free to share v7 when it fits you
> better!

Hi Paolo!
Thanks for reminder, the new version is almost ready, I was a bit 
distracted by other burning issues. Hopefully I'll publish it till the 
end of the week.


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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
       [not found]                         ` <20230502083244.19543d26@kernel.org>
@ 2023-05-03  7:56                             ` Jiri Pirko
  0 siblings, 0 replies; 202+ messages in thread
From: Jiri Pirko @ 2023-05-03  7:56 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Vadim Fedorenko,
	Jonathan Lemon, Paolo Abeni, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

Adding back the cclist stripped due to Claws bug.

Tue, May 02, 2023 at 05:32:44PM CEST, kuba@kernel.org wrote:
>On Tue, 2 May 2023 10:52:57 +0200 Jiri Pirko wrote:
>> >> Index internal within a single instance. Like Intel guys, they have 1
>> >> clock wired up with multiple DPLLs. The driver gives every DPLL index.
>> >> This is internal, totally up to the driver decision. Similar concept to
>> >> devlink port index.  
>> >
>> >devlink port index ended up as a pretty odd beast with drivers encoding
>> >various information into it, using locally grown schemes.
>> >
>> >Hard no on doing that in dpll, it should not be exposed to the user.  
>> 
>> So you say to have ID fully dynamic and non deterministic? I'm lost a
>> bit.
>
>Yup, non-deterministic, just a cyclic ID allocated by the core starting
>from 1. Finding the right device / pin needs to be done via
>informational attributes not making assumptions about the ID.

Okay.

When netdev will have pin ID in the RT netlink message (as it is done
in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.

However, for non-SyncE usecase, how do you imagine scripts to work?
I mean, the script have to obtain dpll/pin ID by deterministic
module_name/clock_id/idx tuple.

There are 2 options to do that:
1) dump all dplls/pins and do lookup in userspace
2) get a dpll/pin according to given module_name/clock_id/idx tuple

The first approach is not very nice.
The currently pushed RFCv7 of the patchset does not support 2)

Now if we add support for 2), we basically use module_name/clock_id/idx
as a handle for "get cmd". My point is, why can't we use it for "set
cmd" as well and avoid the ID entirely?

What am I missing here?

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

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

Adding back the cclist stripped due to Claws bug.

Tue, May 02, 2023 at 05:32:44PM CEST, kuba@kernel.org wrote:
>On Tue, 2 May 2023 10:52:57 +0200 Jiri Pirko wrote:
>> >> Index internal within a single instance. Like Intel guys, they have 1
>> >> clock wired up with multiple DPLLs. The driver gives every DPLL index.
>> >> This is internal, totally up to the driver decision. Similar concept to
>> >> devlink port index.  
>> >
>> >devlink port index ended up as a pretty odd beast with drivers encoding
>> >various information into it, using locally grown schemes.
>> >
>> >Hard no on doing that in dpll, it should not be exposed to the user.  
>> 
>> So you say to have ID fully dynamic and non deterministic? I'm lost a
>> bit.
>
>Yup, non-deterministic, just a cyclic ID allocated by the core starting
>from 1. Finding the right device / pin needs to be done via
>informational attributes not making assumptions about the ID.

Okay.

When netdev will have pin ID in the RT netlink message (as it is done
in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.

However, for non-SyncE usecase, how do you imagine scripts to work?
I mean, the script have to obtain dpll/pin ID by deterministic
module_name/clock_id/idx tuple.

There are 2 options to do that:
1) dump all dplls/pins and do lookup in userspace
2) get a dpll/pin according to given module_name/clock_id/idx tuple

The first approach is not very nice.
The currently pushed RFCv7 of the patchset does not support 2)

Now if we add support for 2), we basically use module_name/clock_id/idx
as a handle for "get cmd". My point is, why can't we use it for "set
cmd" as well and avoid the ID entirely?

What am I missing 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] 202+ messages in thread

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

On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
> >from 1. Finding the right device / pin needs to be done via
> >informational attributes not making assumptions about the ID.  
> 
> Okay.
> 
> When netdev will have pin ID in the RT netlink message (as it is done
> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
> 
> However, for non-SyncE usecase, how do you imagine scripts to work?
> I mean, the script have to obtain dpll/pin ID by deterministic
> module_name/clock_id/idx tuple.

No scoped idx.

> There are 2 options to do that:
> 1) dump all dplls/pins and do lookup in userspace
> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
> 
> The first approach is not very nice.
> The currently pushed RFCv7 of the patchset does not support 2)
> 
> Now if we add support for 2), we basically use module_name/clock_id/idx
> as a handle for "get cmd". My point is, why can't we use it for "set
> cmd" as well and avoid the ID entirely?

Sure, we don't _have_ to have an ID, but it seems go against normal
data normalization rules. And I don't see any harm in it.

But you're asking for per-device "idx" and that's a no-go for me,
given already cited experience.

The user space can look up the ID based on identifying information it
has. IMO it's better to support multiple different intelligible elements
than single integer index into which drivers will start encoding all
sort of info, using locally invented schemes.

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

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

On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
> >from 1. Finding the right device / pin needs to be done via
> >informational attributes not making assumptions about the ID.  
> 
> Okay.
> 
> When netdev will have pin ID in the RT netlink message (as it is done
> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
> 
> However, for non-SyncE usecase, how do you imagine scripts to work?
> I mean, the script have to obtain dpll/pin ID by deterministic
> module_name/clock_id/idx tuple.

No scoped idx.

> There are 2 options to do that:
> 1) dump all dplls/pins and do lookup in userspace
> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
> 
> The first approach is not very nice.
> The currently pushed RFCv7 of the patchset does not support 2)
> 
> Now if we add support for 2), we basically use module_name/clock_id/idx
> as a handle for "get cmd". My point is, why can't we use it for "set
> cmd" as well and avoid the ID entirely?

Sure, we don't _have_ to have an ID, but it seems go against normal
data normalization rules. And I don't see any harm in it.

But you're asking for per-device "idx" and that's a no-go for me,
given already cited experience.

The user space can look up the ID based on identifying information it
has. IMO it's better to support multiple different intelligible elements
than single integer index into which drivers will start encoding all
sort of info, using locally invented schemes.

_______________________________________________
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] 202+ messages in thread

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

Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
>> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
>> >from 1. Finding the right device / pin needs to be done via
>> >informational attributes not making assumptions about the ID.  
>> 
>> Okay.
>> 
>> When netdev will have pin ID in the RT netlink message (as it is done
>> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>> 
>> However, for non-SyncE usecase, how do you imagine scripts to work?
>> I mean, the script have to obtain dpll/pin ID by deterministic
>> module_name/clock_id/idx tuple.
>
>No scoped idx.

That means, no index defined by a driver if I undestand you correctly,
right?


>
>> There are 2 options to do that:
>> 1) dump all dplls/pins and do lookup in userspace
>> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>> 
>> The first approach is not very nice.
>> The currently pushed RFCv7 of the patchset does not support 2)
>> 
>> Now if we add support for 2), we basically use module_name/clock_id/idx
>> as a handle for "get cmd". My point is, why can't we use it for "set
>> cmd" as well and avoid the ID entirely?
>
>Sure, we don't _have_ to have an ID, but it seems go against normal
>data normalization rules. And I don't see any harm in it.
>
>But you're asking for per-device "idx" and that's a no-go for me,
>given already cited experience.
>
>The user space can look up the ID based on identifying information it
>has. IMO it's better to support multiple different intelligible elements

Do you mean fixed tuple or variable tuple?

CMD_GET_ID
  -> DPLL_A_MODULE_NAME
     DPLL_A_CLOCK_ID
  <- DPLL_A_ID

CMD_GET_PIN_ID
  -> DPLL_A_MODULE_NAME
     DPLL_A_CLOCK_ID
  <- DPLL_A_PIN_ID



>than single integer index into which drivers will start encoding all
>sort of info, using locally invented schemes.

There could be multiple DPLL and pin instances for a single
module/clock_id tuple we have to distinguish somehow. If the driver
can't pass "index" of DPLL or a pin, how we distinguish them?

Plus is is possible that 2 driver instances share the same dpll
instance, then to get the dpll pointer reference, they do:
INSTANCE A:
dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);

INSTANCE B:
dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);

My point is, event if we don't expose the index to the userspace,
we need to have it internally.


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

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

Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
>> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
>> >from 1. Finding the right device / pin needs to be done via
>> >informational attributes not making assumptions about the ID.  
>> 
>> Okay.
>> 
>> When netdev will have pin ID in the RT netlink message (as it is done
>> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>> 
>> However, for non-SyncE usecase, how do you imagine scripts to work?
>> I mean, the script have to obtain dpll/pin ID by deterministic
>> module_name/clock_id/idx tuple.
>
>No scoped idx.

That means, no index defined by a driver if I undestand you correctly,
right?


>
>> There are 2 options to do that:
>> 1) dump all dplls/pins and do lookup in userspace
>> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>> 
>> The first approach is not very nice.
>> The currently pushed RFCv7 of the patchset does not support 2)
>> 
>> Now if we add support for 2), we basically use module_name/clock_id/idx
>> as a handle for "get cmd". My point is, why can't we use it for "set
>> cmd" as well and avoid the ID entirely?
>
>Sure, we don't _have_ to have an ID, but it seems go against normal
>data normalization rules. And I don't see any harm in it.
>
>But you're asking for per-device "idx" and that's a no-go for me,
>given already cited experience.
>
>The user space can look up the ID based on identifying information it
>has. IMO it's better to support multiple different intelligible elements

Do you mean fixed tuple or variable tuple?

CMD_GET_ID
  -> DPLL_A_MODULE_NAME
     DPLL_A_CLOCK_ID
  <- DPLL_A_ID

CMD_GET_PIN_ID
  -> DPLL_A_MODULE_NAME
     DPLL_A_CLOCK_ID
  <- DPLL_A_PIN_ID



>than single integer index into which drivers will start encoding all
>sort of info, using locally invented schemes.

There could be multiple DPLL and pin instances for a single
module/clock_id tuple we have to distinguish somehow. If the driver
can't pass "index" of DPLL or a pin, how we distinguish them?

Plus is is possible that 2 driver instances share the same dpll
instance, then to get the dpll pointer reference, they do:
INSTANCE A:
dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);

INSTANCE B:
dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);

My point is, event if we don't expose the index to the userspace,
we need to have it internally.


_______________________________________________
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] 202+ messages in thread

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

Thu, May 04, 2023 at 01:00:42PM CEST, jiri@resnulli.us wrote:
>Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>>On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
>>> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
>>> >from 1. Finding the right device / pin needs to be done via
>>> >informational attributes not making assumptions about the ID.  
>>> 
>>> Okay.
>>> 
>>> When netdev will have pin ID in the RT netlink message (as it is done
>>> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>>> 
>>> However, for non-SyncE usecase, how do you imagine scripts to work?
>>> I mean, the script have to obtain dpll/pin ID by deterministic
>>> module_name/clock_id/idx tuple.
>>
>>No scoped idx.
>
>That means, no index defined by a driver if I undestand you correctly,
>right?
>
>
>>
>>> There are 2 options to do that:
>>> 1) dump all dplls/pins and do lookup in userspace
>>> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>>> 
>>> The first approach is not very nice.
>>> The currently pushed RFCv7 of the patchset does not support 2)
>>> 
>>> Now if we add support for 2), we basically use module_name/clock_id/idx
>>> as a handle for "get cmd". My point is, why can't we use it for "set
>>> cmd" as well and avoid the ID entirely?
>>
>>Sure, we don't _have_ to have an ID, but it seems go against normal
>>data normalization rules. And I don't see any harm in it.
>>
>>But you're asking for per-device "idx" and that's a no-go for me,
>>given already cited experience.
>>
>>The user space can look up the ID based on identifying information it
>>has. IMO it's better to support multiple different intelligible elements
>
>Do you mean fixed tuple or variable tuple?
>
>CMD_GET_ID
>  -> DPLL_A_MODULE_NAME
>     DPLL_A_CLOCK_ID

Sorry, I hit the send button by a mistake.
I ment to add a question here:
What is the next intelligible element to identify DPLL device here?

>  <- DPLL_A_ID
>
>CMD_GET_PIN_ID
>  -> DPLL_A_MODULE_NAME
>     DPLL_A_CLOCK_ID

What is the next intelligible element to identify a pin here?

>  <- DPLL_A_PIN_ID
>
>
>
>>than single integer index into which drivers will start encoding all
>>sort of info, using locally invented schemes.
>
>There could be multiple DPLL and pin instances for a single
>module/clock_id tuple we have to distinguish somehow. If the driver
>can't pass "index" of DPLL or a pin, how we distinguish them?
>
>Plus is is possible that 2 driver instances share the same dpll
>instance, then to get the dpll pointer reference, they do:
>INSTANCE A:
>dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>
>INSTANCE B:
>dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>
>My point is, event if we don't expose the index to the userspace,
>we need to have it internally.
>

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

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

Thu, May 04, 2023 at 01:00:42PM CEST, jiri@resnulli.us wrote:
>Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>>On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:
>>> >Yup, non-deterministic, just a cyclic ID allocated by the core starting
>>> >from 1. Finding the right device / pin needs to be done via
>>> >informational attributes not making assumptions about the ID.  
>>> 
>>> Okay.
>>> 
>>> When netdev will have pin ID in the RT netlink message (as it is done
>>> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>>> 
>>> However, for non-SyncE usecase, how do you imagine scripts to work?
>>> I mean, the script have to obtain dpll/pin ID by deterministic
>>> module_name/clock_id/idx tuple.
>>
>>No scoped idx.
>
>That means, no index defined by a driver if I undestand you correctly,
>right?
>
>
>>
>>> There are 2 options to do that:
>>> 1) dump all dplls/pins and do lookup in userspace
>>> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>>> 
>>> The first approach is not very nice.
>>> The currently pushed RFCv7 of the patchset does not support 2)
>>> 
>>> Now if we add support for 2), we basically use module_name/clock_id/idx
>>> as a handle for "get cmd". My point is, why can't we use it for "set
>>> cmd" as well and avoid the ID entirely?
>>
>>Sure, we don't _have_ to have an ID, but it seems go against normal
>>data normalization rules. And I don't see any harm in it.
>>
>>But you're asking for per-device "idx" and that's a no-go for me,
>>given already cited experience.
>>
>>The user space can look up the ID based on identifying information it
>>has. IMO it's better to support multiple different intelligible elements
>
>Do you mean fixed tuple or variable tuple?
>
>CMD_GET_ID
>  -> DPLL_A_MODULE_NAME
>     DPLL_A_CLOCK_ID

Sorry, I hit the send button by a mistake.
I ment to add a question here:
What is the next intelligible element to identify DPLL device here?

>  <- DPLL_A_ID
>
>CMD_GET_PIN_ID
>  -> DPLL_A_MODULE_NAME
>     DPLL_A_CLOCK_ID

What is the next intelligible element to identify a pin here?

>  <- DPLL_A_PIN_ID
>
>
>
>>than single integer index into which drivers will start encoding all
>>sort of info, using locally invented schemes.
>
>There could be multiple DPLL and pin instances for a single
>module/clock_id tuple we have to distinguish somehow. If the driver
>can't pass "index" of DPLL or a pin, how we distinguish them?
>
>Plus is is possible that 2 driver instances share the same dpll
>instance, then to get the dpll pointer reference, they do:
>INSTANCE A:
>dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>
>INSTANCE B:
>dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>
>My point is, event if we don't expose the index to the userspace,
>we need to have it internally.
>

_______________________________________________
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] 202+ messages in thread

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

On Thu, 4 May 2023 13:00:42 +0200 Jiri Pirko wrote:
> Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
> >On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:  
> >> Okay.
> >> 
> >> When netdev will have pin ID in the RT netlink message (as it is done
> >> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
> >> 
> >> However, for non-SyncE usecase, how do you imagine scripts to work?
> >> I mean, the script have to obtain dpll/pin ID by deterministic
> >> module_name/clock_id/idx tuple.  
> >
> >No scoped idx.  
> 
> That means, no index defined by a driver if I undestand you correctly,
> right?

Yes, my suggestion did not include a scoped index with no
globally defined semantics.
 
> >> There are 2 options to do that:
> >> 1) dump all dplls/pins and do lookup in userspace
> >> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
> >> 
> >> The first approach is not very nice.
> >> The currently pushed RFCv7 of the patchset does not support 2)
> >> 
> >> Now if we add support for 2), we basically use module_name/clock_id/idx
> >> as a handle for "get cmd". My point is, why can't we use it for "set
> >> cmd" as well and avoid the ID entirely?  
> >
> >Sure, we don't _have_ to have an ID, but it seems go against normal
> >data normalization rules. And I don't see any harm in it.
> >
> >But you're asking for per-device "idx" and that's a no-go for me,
> >given already cited experience.
> >
> >The user space can look up the ID based on identifying information it
> >has. IMO it's better to support multiple different intelligible elements  
> 
> Do you mean fixed tuple or variable tuple?
> 
> CMD_GET_ID
>   -> DPLL_A_MODULE_NAME  
>      DPLL_A_CLOCK_ID

> What is the next intelligible element to identify DPLL device here?

I don't know. We can always add more as needed.
We presuppose that the devices are identifiable, so whatever info
is used to identify them goes here.

>   <- DPLL_A_ID
> 
> CMD_GET_PIN_ID
>   -> DPLL_A_MODULE_NAME  
>      DPLL_A_CLOCK_ID

> What is the next intelligible element to identify a pin here?

Same answer. Could be a name of the pin according to ASIC docs.
Could be the ball name for a BGA package. Anything that's meaningful.

My point is that we don't want a field simply called "index". Because
then for one vendor it will mean Ethernet port, for another SMA
connector number and for the third pin of the package. Those are
different attributes.

>   <- DPLL_A_PIN_ID
> 
> >than single integer index into which drivers will start encoding all
> >sort of info, using locally invented schemes.  
> 
> There could be multiple DPLL and pin instances for a single
> module/clock_id tuple we have to distinguish somehow. If the driver
> can't pass "index" of DPLL or a pin, how we distinguish them?
> 
> Plus is is possible that 2 driver instances share the same dpll
> instance, then to get the dpll pointer reference, they do:
> INSTANCE A:
> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
> 
> INSTANCE B:
> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
> 
> My point is, event if we don't expose the index to the userspace,
> we need to have it internally.

That's fine, I guess. I'd prefer driver matching to be the same as user
space matching to force driver authors to have the same perspective as
the user. But a "driver coookie" not visible to user space it probably
fine.

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

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

On Thu, 4 May 2023 13:00:42 +0200 Jiri Pirko wrote:
> Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
> >On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:  
> >> Okay.
> >> 
> >> When netdev will have pin ID in the RT netlink message (as it is done
> >> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
> >> 
> >> However, for non-SyncE usecase, how do you imagine scripts to work?
> >> I mean, the script have to obtain dpll/pin ID by deterministic
> >> module_name/clock_id/idx tuple.  
> >
> >No scoped idx.  
> 
> That means, no index defined by a driver if I undestand you correctly,
> right?

Yes, my suggestion did not include a scoped index with no
globally defined semantics.
 
> >> There are 2 options to do that:
> >> 1) dump all dplls/pins and do lookup in userspace
> >> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
> >> 
> >> The first approach is not very nice.
> >> The currently pushed RFCv7 of the patchset does not support 2)
> >> 
> >> Now if we add support for 2), we basically use module_name/clock_id/idx
> >> as a handle for "get cmd". My point is, why can't we use it for "set
> >> cmd" as well and avoid the ID entirely?  
> >
> >Sure, we don't _have_ to have an ID, but it seems go against normal
> >data normalization rules. And I don't see any harm in it.
> >
> >But you're asking for per-device "idx" and that's a no-go for me,
> >given already cited experience.
> >
> >The user space can look up the ID based on identifying information it
> >has. IMO it's better to support multiple different intelligible elements  
> 
> Do you mean fixed tuple or variable tuple?
> 
> CMD_GET_ID
>   -> DPLL_A_MODULE_NAME  
>      DPLL_A_CLOCK_ID

> What is the next intelligible element to identify DPLL device here?

I don't know. We can always add more as needed.
We presuppose that the devices are identifiable, so whatever info
is used to identify them goes here.

>   <- DPLL_A_ID
> 
> CMD_GET_PIN_ID
>   -> DPLL_A_MODULE_NAME  
>      DPLL_A_CLOCK_ID

> What is the next intelligible element to identify a pin here?

Same answer. Could be a name of the pin according to ASIC docs.
Could be the ball name for a BGA package. Anything that's meaningful.

My point is that we don't want a field simply called "index". Because
then for one vendor it will mean Ethernet port, for another SMA
connector number and for the third pin of the package. Those are
different attributes.

>   <- DPLL_A_PIN_ID
> 
> >than single integer index into which drivers will start encoding all
> >sort of info, using locally invented schemes.  
> 
> There could be multiple DPLL and pin instances for a single
> module/clock_id tuple we have to distinguish somehow. If the driver
> can't pass "index" of DPLL or a pin, how we distinguish them?
> 
> Plus is is possible that 2 driver instances share the same dpll
> instance, then to get the dpll pointer reference, they do:
> INSTANCE A:
> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
> 
> INSTANCE B:
> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
> 
> My point is, event if we don't expose the index to the userspace,
> we need to have it internally.

That's fine, I guess. I'd prefer driver matching to be the same as user
space matching to force driver authors to have the same perspective as
the user. But a "driver coookie" not visible to user space it probably
fine.

_______________________________________________
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] 202+ messages in thread

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

Thu, May 04, 2023 at 06:04:01PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 13:00:42 +0200 Jiri Pirko wrote:
>> Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>> >On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:  
>> >> Okay.
>> >> 
>> >> When netdev will have pin ID in the RT netlink message (as it is done
>> >> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>> >> 
>> >> However, for non-SyncE usecase, how do you imagine scripts to work?
>> >> I mean, the script have to obtain dpll/pin ID by deterministic
>> >> module_name/clock_id/idx tuple.  
>> >
>> >No scoped idx.  
>> 
>> That means, no index defined by a driver if I undestand you correctly,
>> right?
>
>Yes, my suggestion did not include a scoped index with no
>globally defined semantics.

Okay, makes sense. Devlink port index didn't end up well :/


> 
>> >> There are 2 options to do that:
>> >> 1) dump all dplls/pins and do lookup in userspace
>> >> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>> >> 
>> >> The first approach is not very nice.
>> >> The currently pushed RFCv7 of the patchset does not support 2)
>> >> 
>> >> Now if we add support for 2), we basically use module_name/clock_id/idx
>> >> as a handle for "get cmd". My point is, why can't we use it for "set
>> >> cmd" as well and avoid the ID entirely?  
>> >
>> >Sure, we don't _have_ to have an ID, but it seems go against normal
>> >data normalization rules. And I don't see any harm in it.
>> >
>> >But you're asking for per-device "idx" and that's a no-go for me,
>> >given already cited experience.
>> >
>> >The user space can look up the ID based on identifying information it
>> >has. IMO it's better to support multiple different intelligible elements  
>> 
>> Do you mean fixed tuple or variable tuple?
>> 
>> CMD_GET_ID
>>   -> DPLL_A_MODULE_NAME  
>>      DPLL_A_CLOCK_ID
>
>> What is the next intelligible element to identify DPLL device here?
>
>I don't know. We can always add more as needed.
>We presuppose that the devices are identifiable, so whatever info
>is used to identify them goes here.

Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
distinction between the 2 dpll instances there

So for now, we can have:
 CMD_GET_ID
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_TYPE
   <- DPLL_A_ID


if user passes a subset which would not provide a single match, we error
out with -EINVAL and proper exack message. Makes sense?





>
>>   <- DPLL_A_ID
>> 
>> CMD_GET_PIN_ID
>>   -> DPLL_A_MODULE_NAME  
>>      DPLL_A_CLOCK_ID
>
>> What is the next intelligible element to identify a pin here?
>
>Same answer. Could be a name of the pin according to ASIC docs.
>Could be the ball name for a BGA package. Anything that's meaningful.

Okay, for pin, the type and label would probably do:
 CMD_GET_PIN_ID
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_PIN_TYPE
      DPLL_A_PIN_LABEL
   <- DPLL_A_PIN_ID

Again, if user passes a subset which would not provide a single match,
we error out with -EINVAL and proper exack message.

If there is only one pin for example, user query of DPLL_A_MODULE_NAME
and DPLL_A_CLOCK_ID would do return a single match. No need to pass
anything else.

I think this could work with both ice and ptp_ocp, correct guys?

For mlx5, I will have 2 or more pins with same module name, clock id
and type. For these SyncE pins the label does not really make sense.
But I don't have to query, because the PIN_ID is going to be exposed for
netdev over RT netlink. Clicks.

Makes sense?


>
>My point is that we don't want a field simply called "index". Because
>then for one vendor it will mean Ethernet port, for another SMA
>connector number and for the third pin of the package. Those are
>different attributes.

Got you and agree.


>
>>   <- DPLL_A_PIN_ID
>> 
>> >than single integer index into which drivers will start encoding all
>> >sort of info, using locally invented schemes.  
>> 
>> There could be multiple DPLL and pin instances for a single
>> module/clock_id tuple we have to distinguish somehow. If the driver
>> can't pass "index" of DPLL or a pin, how we distinguish them?
>> 
>> Plus is is possible that 2 driver instances share the same dpll
>> instance, then to get the dpll pointer reference, they do:
>> INSTANCE A:
>> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>> 
>> INSTANCE B:
>> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>> 
>> My point is, event if we don't expose the index to the userspace,
>> we need to have it internally.
>
>That's fine, I guess. I'd prefer driver matching to be the same as user
>space matching to force driver authors to have the same perspective as
>the user. But a "driver coookie" not visible to user space it probably
>fine.

Allright, lets leave them for now. As internal kernel API, could be
changed in the future if needed.

Arkadiusz, Vadim, are you following this?


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

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

Thu, May 04, 2023 at 06:04:01PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 13:00:42 +0200 Jiri Pirko wrote:
>> Thu, May 04, 2023 at 04:16:43AM CEST, kuba@kernel.org wrote:
>> >On Wed, 3 May 2023 09:56:57 +0200 Jiri Pirko wrote:  
>> >> Okay.
>> >> 
>> >> When netdev will have pin ID in the RT netlink message (as it is done
>> >> in RFCv7), it is easy to get the pin/dpll for netdev. No problem there.
>> >> 
>> >> However, for non-SyncE usecase, how do you imagine scripts to work?
>> >> I mean, the script have to obtain dpll/pin ID by deterministic
>> >> module_name/clock_id/idx tuple.  
>> >
>> >No scoped idx.  
>> 
>> That means, no index defined by a driver if I undestand you correctly,
>> right?
>
>Yes, my suggestion did not include a scoped index with no
>globally defined semantics.

Okay, makes sense. Devlink port index didn't end up well :/


> 
>> >> There are 2 options to do that:
>> >> 1) dump all dplls/pins and do lookup in userspace
>> >> 2) get a dpll/pin according to given module_name/clock_id/idx tuple
>> >> 
>> >> The first approach is not very nice.
>> >> The currently pushed RFCv7 of the patchset does not support 2)
>> >> 
>> >> Now if we add support for 2), we basically use module_name/clock_id/idx
>> >> as a handle for "get cmd". My point is, why can't we use it for "set
>> >> cmd" as well and avoid the ID entirely?  
>> >
>> >Sure, we don't _have_ to have an ID, but it seems go against normal
>> >data normalization rules. And I don't see any harm in it.
>> >
>> >But you're asking for per-device "idx" and that's a no-go for me,
>> >given already cited experience.
>> >
>> >The user space can look up the ID based on identifying information it
>> >has. IMO it's better to support multiple different intelligible elements  
>> 
>> Do you mean fixed tuple or variable tuple?
>> 
>> CMD_GET_ID
>>   -> DPLL_A_MODULE_NAME  
>>      DPLL_A_CLOCK_ID
>
>> What is the next intelligible element to identify DPLL device here?
>
>I don't know. We can always add more as needed.
>We presuppose that the devices are identifiable, so whatever info
>is used to identify them goes here.

Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
distinction between the 2 dpll instances there

So for now, we can have:
 CMD_GET_ID
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_TYPE
   <- DPLL_A_ID


if user passes a subset which would not provide a single match, we error
out with -EINVAL and proper exack message. Makes sense?





>
>>   <- DPLL_A_ID
>> 
>> CMD_GET_PIN_ID
>>   -> DPLL_A_MODULE_NAME  
>>      DPLL_A_CLOCK_ID
>
>> What is the next intelligible element to identify a pin here?
>
>Same answer. Could be a name of the pin according to ASIC docs.
>Could be the ball name for a BGA package. Anything that's meaningful.

Okay, for pin, the type and label would probably do:
 CMD_GET_PIN_ID
   -> DPLL_A_MODULE_NAME
      DPLL_A_CLOCK_ID
      DPLL_A_PIN_TYPE
      DPLL_A_PIN_LABEL
   <- DPLL_A_PIN_ID

Again, if user passes a subset which would not provide a single match,
we error out with -EINVAL and proper exack message.

If there is only one pin for example, user query of DPLL_A_MODULE_NAME
and DPLL_A_CLOCK_ID would do return a single match. No need to pass
anything else.

I think this could work with both ice and ptp_ocp, correct guys?

For mlx5, I will have 2 or more pins with same module name, clock id
and type. For these SyncE pins the label does not really make sense.
But I don't have to query, because the PIN_ID is going to be exposed for
netdev over RT netlink. Clicks.

Makes sense?


>
>My point is that we don't want a field simply called "index". Because
>then for one vendor it will mean Ethernet port, for another SMA
>connector number and for the third pin of the package. Those are
>different attributes.

Got you and agree.


>
>>   <- DPLL_A_PIN_ID
>> 
>> >than single integer index into which drivers will start encoding all
>> >sort of info, using locally invented schemes.  
>> 
>> There could be multiple DPLL and pin instances for a single
>> module/clock_id tuple we have to distinguish somehow. If the driver
>> can't pass "index" of DPLL or a pin, how we distinguish them?
>> 
>> Plus is is possible that 2 driver instances share the same dpll
>> instance, then to get the dpll pointer reference, they do:
>> INSTANCE A:
>> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>> 
>> INSTANCE B:
>> dpll_0 = dpll_device_get(clock_id, 0, THIS_MODULE);
>> dpll_1 = dpll_device_get(clock_id, 1, THIS_MODULE);
>> 
>> My point is, event if we don't expose the index to the userspace,
>> we need to have it internally.
>
>That's fine, I guess. I'd prefer driver matching to be the same as user
>space matching to force driver authors to have the same perspective as
>the user. But a "driver coookie" not visible to user space it probably
>fine.

Allright, lets leave them for now. As internal kernel API, could be
changed in the future if needed.

Arkadiusz, Vadim, are you following this?


_______________________________________________
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] 202+ messages in thread

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

On Thu, 4 May 2023 19:51:38 +0200 Jiri Pirko wrote:
> >> What is the next intelligible element to identify DPLL device here?  
> >
> >I don't know. We can always add more as needed.
> >We presuppose that the devices are identifiable, so whatever info
> >is used to identify them goes here.  
> 
> Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
> are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
> distinction between the 2 dpll instances there
> 
> So for now, we can have:
>  CMD_GET_ID
>    -> DPLL_A_MODULE_NAME  
>       DPLL_A_CLOCK_ID
>       DPLL_A_TYPE
>    <- DPLL_A_ID
> 
> 
> if user passes a subset which would not provide a single match, we error
> out with -EINVAL and proper exack message. Makes sense?

Yup, that sounds good to me.

> >Same answer. Could be a name of the pin according to ASIC docs.
> >Could be the ball name for a BGA package. Anything that's meaningful.  
> 
> Okay, for pin, the type and label would probably do:
>  CMD_GET_PIN_ID
>    -> DPLL_A_MODULE_NAME  
>       DPLL_A_CLOCK_ID
>       DPLL_A_PIN_TYPE
>       DPLL_A_PIN_LABEL

Label sounds dangerously open ended, too. Would that be the SMA
connector label (i.e. front panel label)? Or also applicable to
internal pins? It'd be easier to talk details if we had the user
facing documentation that ships with these products.

>    <- DPLL_A_PIN_ID
> 
> Again, if user passes a subset which would not provide a single match,
> we error out with -EINVAL and proper exack message.
> 
> If there is only one pin for example, user query of DPLL_A_MODULE_NAME
> and DPLL_A_CLOCK_ID would do return a single match. No need to pass
> anything else.
> 
> I think this could work with both ice and ptp_ocp, correct guys?
> 
> For mlx5, I will have 2 or more pins with same module name, clock id
> and type. For these SyncE pins the label does not really make sense.
> But I don't have to query, because the PIN_ID is going to be exposed for
> netdev over RT netlink. Clicks.
> 
> Makes sense?

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

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

On Thu, 4 May 2023 19:51:38 +0200 Jiri Pirko wrote:
> >> What is the next intelligible element to identify DPLL device here?  
> >
> >I don't know. We can always add more as needed.
> >We presuppose that the devices are identifiable, so whatever info
> >is used to identify them goes here.  
> 
> Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
> are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
> distinction between the 2 dpll instances there
> 
> So for now, we can have:
>  CMD_GET_ID
>    -> DPLL_A_MODULE_NAME  
>       DPLL_A_CLOCK_ID
>       DPLL_A_TYPE
>    <- DPLL_A_ID
> 
> 
> if user passes a subset which would not provide a single match, we error
> out with -EINVAL and proper exack message. Makes sense?

Yup, that sounds good to me.

> >Same answer. Could be a name of the pin according to ASIC docs.
> >Could be the ball name for a BGA package. Anything that's meaningful.  
> 
> Okay, for pin, the type and label would probably do:
>  CMD_GET_PIN_ID
>    -> DPLL_A_MODULE_NAME  
>       DPLL_A_CLOCK_ID
>       DPLL_A_PIN_TYPE
>       DPLL_A_PIN_LABEL

Label sounds dangerously open ended, too. Would that be the SMA
connector label (i.e. front panel label)? Or also applicable to
internal pins? It'd be easier to talk details if we had the user
facing documentation that ships with these products.

>    <- DPLL_A_PIN_ID
> 
> Again, if user passes a subset which would not provide a single match,
> we error out with -EINVAL and proper exack message.
> 
> If there is only one pin for example, user query of DPLL_A_MODULE_NAME
> and DPLL_A_CLOCK_ID would do return a single match. No need to pass
> anything else.
> 
> I think this could work with both ice and ptp_ocp, correct guys?
> 
> For mlx5, I will have 2 or more pins with same module name, clock id
> and type. For these SyncE pins the label does not really make sense.
> But I don't have to query, because the PIN_ID is going to be exposed for
> netdev over RT netlink. Clicks.
> 
> Makes sense?

_______________________________________________
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] 202+ messages in thread

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

Thu, May 04, 2023 at 08:44:21PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 19:51:38 +0200 Jiri Pirko wrote:
>> >> What is the next intelligible element to identify DPLL device here?  
>> >
>> >I don't know. We can always add more as needed.
>> >We presuppose that the devices are identifiable, so whatever info
>> >is used to identify them goes here.  
>> 
>> Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
>> are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
>> distinction between the 2 dpll instances there
>> 
>> So for now, we can have:
>>  CMD_GET_ID
>>    -> DPLL_A_MODULE_NAME  
>>       DPLL_A_CLOCK_ID
>>       DPLL_A_TYPE
>>    <- DPLL_A_ID
>> 
>> 
>> if user passes a subset which would not provide a single match, we error
>> out with -EINVAL and proper exack message. Makes sense?
>
>Yup, that sounds good to me.
>
>> >Same answer. Could be a name of the pin according to ASIC docs.
>> >Could be the ball name for a BGA package. Anything that's meaningful.  
>> 
>> Okay, for pin, the type and label would probably do:
>>  CMD_GET_PIN_ID
>>    -> DPLL_A_MODULE_NAME  
>>       DPLL_A_CLOCK_ID
>>       DPLL_A_PIN_TYPE
>>       DPLL_A_PIN_LABEL
>
>Label sounds dangerously open ended, too. Would that be the SMA

Well, every string is. And past RFCs of this patchset demonstrated guys
did serialize a lot of stuff in strings.


>connector label (i.e. front panel label)? Or also applicable to
>internal pins? It'd be easier to talk details if we had the user
>facing documentation that ships with these products.

I think is is use case specific. Some of the pins face the user over
physical port, they it is a front panel label. Others are internal
names. I have no clue how to define and mainly enforce rules here.

But as an example, if you have 2 pins of the same type, only difference
is they are connected to front panel connector "A" and "B", this is the
label you have to pass to the ID query. Do you see any other way?


>
>>    <- DPLL_A_PIN_ID
>> 
>> Again, if user passes a subset which would not provide a single match,
>> we error out with -EINVAL and proper exack message.
>> 
>> If there is only one pin for example, user query of DPLL_A_MODULE_NAME
>> and DPLL_A_CLOCK_ID would do return a single match. No need to pass
>> anything else.
>> 
>> I think this could work with both ice and ptp_ocp, correct guys?
>> 
>> For mlx5, I will have 2 or more pins with same module name, clock id
>> and type. For these SyncE pins the label does not really make sense.
>> But I don't have to query, because the PIN_ID is going to be exposed for
>> netdev over RT netlink. Clicks.
>> 
>> Makes sense?

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

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

Thu, May 04, 2023 at 08:44:21PM CEST, kuba@kernel.org wrote:
>On Thu, 4 May 2023 19:51:38 +0200 Jiri Pirko wrote:
>> >> What is the next intelligible element to identify DPLL device here?  
>> >
>> >I don't know. We can always add more as needed.
>> >We presuppose that the devices are identifiable, so whatever info
>> >is used to identify them goes here.  
>> 
>> Allright. So in case of ptp_ocp and mlx5, module_name and clock_id
>> are enough. In case of ice, DPLL_A_TYPE, attr is the one to make
>> distinction between the 2 dpll instances there
>> 
>> So for now, we can have:
>>  CMD_GET_ID
>>    -> DPLL_A_MODULE_NAME  
>>       DPLL_A_CLOCK_ID
>>       DPLL_A_TYPE
>>    <- DPLL_A_ID
>> 
>> 
>> if user passes a subset which would not provide a single match, we error
>> out with -EINVAL and proper exack message. Makes sense?
>
>Yup, that sounds good to me.
>
>> >Same answer. Could be a name of the pin according to ASIC docs.
>> >Could be the ball name for a BGA package. Anything that's meaningful.  
>> 
>> Okay, for pin, the type and label would probably do:
>>  CMD_GET_PIN_ID
>>    -> DPLL_A_MODULE_NAME  
>>       DPLL_A_CLOCK_ID
>>       DPLL_A_PIN_TYPE
>>       DPLL_A_PIN_LABEL
>
>Label sounds dangerously open ended, too. Would that be the SMA

Well, every string is. And past RFCs of this patchset demonstrated guys
did serialize a lot of stuff in strings.


>connector label (i.e. front panel label)? Or also applicable to
>internal pins? It'd be easier to talk details if we had the user
>facing documentation that ships with these products.

I think is is use case specific. Some of the pins face the user over
physical port, they it is a front panel label. Others are internal
names. I have no clue how to define and mainly enforce rules here.

But as an example, if you have 2 pins of the same type, only difference
is they are connected to front panel connector "A" and "B", this is the
label you have to pass to the ID query. Do you see any other way?


>
>>    <- DPLL_A_PIN_ID
>> 
>> Again, if user passes a subset which would not provide a single match,
>> we error out with -EINVAL and proper exack message.
>> 
>> If there is only one pin for example, user query of DPLL_A_MODULE_NAME
>> and DPLL_A_CLOCK_ID would do return a single match. No need to pass
>> anything else.
>> 
>> I think this could work with both ice and ptp_ocp, correct guys?
>> 
>> For mlx5, I will have 2 or more pins with same module name, clock id
>> and type. For these SyncE pins the label does not really make sense.
>> But I don't have to query, because the PIN_ID is going to be exposed for
>> netdev over RT netlink. Clicks.
>> 
>> Makes sense?

_______________________________________________
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] 202+ messages in thread

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

On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
> >connector label (i.e. front panel label)? Or also applicable to
> >internal pins? It'd be easier to talk details if we had the user
> >facing documentation that ships with these products.  
> 
> I think is is use case specific. Some of the pins face the user over
> physical port, they it is a front panel label. Others are internal
> names. I have no clue how to define and mainly enforce rules here.

It should be pretty easy to judge if we see the user-facing
documentation vendors have.

> But as an example, if you have 2 pins of the same type, only difference
> is they are connected to front panel connector "A" and "B", this is the
> label you have to pass to the ID query. Do you see any other way?

Sound perfectly fine, if it's a front panel label, let's call 
the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
brought out to the front panel it will not have this attr.
For other type of labels we should have different attributes.

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

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

On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
> >connector label (i.e. front panel label)? Or also applicable to
> >internal pins? It'd be easier to talk details if we had the user
> >facing documentation that ships with these products.  
> 
> I think is is use case specific. Some of the pins face the user over
> physical port, they it is a front panel label. Others are internal
> names. I have no clue how to define and mainly enforce rules here.

It should be pretty easy to judge if we see the user-facing
documentation vendors have.

> But as an example, if you have 2 pins of the same type, only difference
> is they are connected to front panel connector "A" and "B", this is the
> label you have to pass to the ID query. Do you see any other way?

Sound perfectly fine, if it's a front panel label, let's call 
the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
brought out to the front panel it will not have this attr.
For other type of labels we should have different attributes.

_______________________________________________
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] 202+ messages in thread

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

Fri, May 05, 2023 at 05:35:31PM CEST, kuba@kernel.org wrote:
>On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
>> >connector label (i.e. front panel label)? Or also applicable to
>> >internal pins? It'd be easier to talk details if we had the user
>> >facing documentation that ships with these products.  
>> 
>> I think is is use case specific. Some of the pins face the user over
>> physical port, they it is a front panel label. Others are internal
>> names. I have no clue how to define and mainly enforce rules here.
>
>It should be pretty easy to judge if we see the user-facing
>documentation vendors have.

Intel, Vadim, do you have such documentation?
As I wrote, for mlx5 the label is not really applicable as the link
netdev->pin is defining what the pin is.


>
>> But as an example, if you have 2 pins of the same type, only difference
>> is they are connected to front panel connector "A" and "B", this is the
>> label you have to pass to the ID query. Do you see any other way?
>
>Sound perfectly fine, if it's a front panel label, let's call 
>the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
>brought out to the front panel it will not have this attr.
>For other type of labels we should have different attributes.

Hmm, that would kind of embed the pin type into attr which feels wrong.
We already have the pin type exposed:
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)
};

It case of front panel pin label, the type is "EXT" as for external pin.



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

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

Fri, May 05, 2023 at 05:35:31PM CEST, kuba@kernel.org wrote:
>On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
>> >connector label (i.e. front panel label)? Or also applicable to
>> >internal pins? It'd be easier to talk details if we had the user
>> >facing documentation that ships with these products.  
>> 
>> I think is is use case specific. Some of the pins face the user over
>> physical port, they it is a front panel label. Others are internal
>> names. I have no clue how to define and mainly enforce rules here.
>
>It should be pretty easy to judge if we see the user-facing
>documentation vendors have.

Intel, Vadim, do you have such documentation?
As I wrote, for mlx5 the label is not really applicable as the link
netdev->pin is defining what the pin is.


>
>> But as an example, if you have 2 pins of the same type, only difference
>> is they are connected to front panel connector "A" and "B", this is the
>> label you have to pass to the ID query. Do you see any other way?
>
>Sound perfectly fine, if it's a front panel label, let's call 
>the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
>brought out to the front panel it will not have this attr.
>For other type of labels we should have different attributes.

Hmm, that would kind of embed the pin type into attr which feels wrong.
We already have the pin type exposed:
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)
};

It case of front panel pin label, the type is "EXT" as for external pin.



_______________________________________________
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] 202+ messages in thread

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-07  7:58                                             ` Jiri Pirko
  (?)
@ 2023-05-08  6:50                                             ` Paolo Abeni
  2023-05-08 12:17                                               ` Jiri Pirko
  -1 siblings, 1 reply; 202+ messages in thread
From: Paolo Abeni @ 2023-05-08  6:50 UTC (permalink / raw)
  To: Jiri Pirko, Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Vadim Fedorenko,
	Jonathan Lemon, poros, mschmidt, netdev, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Sun, 2023-05-07 at 09:58 +0200, Jiri Pirko wrote:
> Fri, May 05, 2023 at 05:35:31PM CEST, kuba@kernel.org wrote:
> > On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
> > > 
> > Sound perfectly fine, if it's a front panel label, let's call 
> > the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
> > brought out to the front panel it will not have this attr.
> > For other type of labels we should have different attributes.
> 
> Hmm, that would kind of embed the pin type into attr which feels wrong.

Looking at the above from a different angle, the
DPLL_A_PIN_FRONT_PANEL_LABEL attribute will be available only for
DPLL_PIN_TYPE_EXT type pins, which looks legit to me - possibly
renaming DPLL_A_PIN_FRONT_PANEL_LABEL as DPLL_A_PIN_EXT_LABEL.

Cheer,

Paolo


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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-08  6:50                                             ` Paolo Abeni
@ 2023-05-08 12:17                                               ` Jiri Pirko
  2023-05-08 19:42                                                 ` Jakub Kicinski
  0 siblings, 1 reply; 202+ messages in thread
From: Jiri Pirko @ 2023-05-08 12:17 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: Jakub Kicinski, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Vadim Fedorenko, Jonathan Lemon, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

Mon, May 08, 2023 at 08:50:09AM CEST, pabeni@redhat.com wrote:
>On Sun, 2023-05-07 at 09:58 +0200, Jiri Pirko wrote:
>> Fri, May 05, 2023 at 05:35:31PM CEST, kuba@kernel.org wrote:
>> > On Fri, 5 May 2023 12:41:11 +0200 Jiri Pirko wrote:
>> > > 
>> > Sound perfectly fine, if it's a front panel label, let's call 
>> > the attribute DPLL_A_PIN_FRONT_PANEL_LABEL. If the pin is not
>> > brought out to the front panel it will not have this attr.
>> > For other type of labels we should have different attributes.
>> 
>> Hmm, that would kind of embed the pin type into attr which feels wrong.
>
>Looking at the above from a different angle, the
>DPLL_A_PIN_FRONT_PANEL_LABEL attribute will be available only for
>DPLL_PIN_TYPE_EXT type pins, which looks legit to me - possibly
>renaming DPLL_A_PIN_FRONT_PANEL_LABEL as DPLL_A_PIN_EXT_LABEL.

Well sure, in case there is no "label" attr for the rest of the types.
Which I believe it is, for the ice implementation in this patchset.
Otherwise, there is no way to distinguish between the pins.
To have multiple attrs for label for multiple pin types does not make
any sense to me, that was my point.


>
>Cheer,
>
>Paolo
>

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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-08 12:17                                               ` Jiri Pirko
@ 2023-05-08 19:42                                                 ` Jakub Kicinski
  2023-05-09  7:53                                                   ` Jiri Pirko
  0 siblings, 1 reply; 202+ messages in thread
From: Jakub Kicinski @ 2023-05-08 19:42 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Paolo Abeni, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Vadim Fedorenko, Jonathan Lemon, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

On Mon, 8 May 2023 14:17:30 +0200 Jiri Pirko wrote:
> >> Hmm, that would kind of embed the pin type into attr which feels wrong.  

An attribute which changes meaning based on a value of another attribute
feels even more wrong.

> >Looking at the above from a different angle, the
> >DPLL_A_PIN_FRONT_PANEL_LABEL attribute will be available only for
> >DPLL_PIN_TYPE_EXT type pins, which looks legit to me - possibly
> >renaming DPLL_A_PIN_FRONT_PANEL_LABEL as DPLL_A_PIN_EXT_LABEL.  

Yup. Even renaming EXT to something that's less.. relative :(

> Well sure, in case there is no "label" attr for the rest of the types.
> Which I believe it is, for the ice implementation in this patchset.
> Otherwise, there is no way to distinguish between the pins.
> To have multiple attrs for label for multiple pin types does not make
> any sense to me, that was my point.

Come on, am I really this bad at explaining this?

If we make a generic "label" attribute driver authors will pack
everything they want to expose to the user into it, and then some.

So we need attributes which will feel *obviously* *wrong* to abuse.

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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-08 19:42                                                 ` Jakub Kicinski
@ 2023-05-09  7:53                                                   ` Jiri Pirko
  2023-05-09 14:52                                                     ` Jakub Kicinski
  0 siblings, 1 reply; 202+ messages in thread
From: Jiri Pirko @ 2023-05-09  7:53 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Paolo Abeni, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Vadim Fedorenko, Jonathan Lemon, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

Mon, May 08, 2023 at 09:42:50PM CEST, kuba@kernel.org wrote:
>On Mon, 8 May 2023 14:17:30 +0200 Jiri Pirko wrote:
>> >> Hmm, that would kind of embed the pin type into attr which feels wrong.  
>
>An attribute which changes meaning based on a value of another attribute
>feels even more wrong.

It wouldn't change meaning, it would be still "a label". Either on a
back of a PCI card or internal pin in a board scheme. Still the same
meaning (for mux type for example).



>
>> >Looking at the above from a different angle, the
>> >DPLL_A_PIN_FRONT_PANEL_LABEL attribute will be available only for
>> >DPLL_PIN_TYPE_EXT type pins, which looks legit to me - possibly
>> >renaming DPLL_A_PIN_FRONT_PANEL_LABEL as DPLL_A_PIN_EXT_LABEL.  
>
>Yup. Even renaming EXT to something that's less.. relative :(

Suggestion?


>
>> Well sure, in case there is no "label" attr for the rest of the types.
>> Which I believe it is, for the ice implementation in this patchset.
>> Otherwise, there is no way to distinguish between the pins.
>> To have multiple attrs for label for multiple pin types does not make
>> any sense to me, that was my point.
>
>Come on, am I really this bad at explaining this?

Or perhaps I'm just slow.


>
>If we make a generic "label" attribute driver authors will pack
>everything they want to expose to the user into it, and then some.

What's difference in generic label string attr and type specific label
string attr. What is stopping driver developers to pack crap in either
of these 2. Perhaps I'm missing something. Could you draw examples?


>
>So we need attributes which will feel *obviously* *wrong* to abuse.

Sure, I get what you say and agree. I'm just trying to find out the
actual attributes :)

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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-09  7:53                                                   ` Jiri Pirko
@ 2023-05-09 14:52                                                     ` Jakub Kicinski
  2023-05-09 15:21                                                       ` Jiri Pirko
  0 siblings, 1 reply; 202+ messages in thread
From: Jakub Kicinski @ 2023-05-09 14:52 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Paolo Abeni, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Vadim Fedorenko, Jonathan Lemon, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

On Tue, 9 May 2023 09:53:07 +0200 Jiri Pirko wrote:
> >Yup. Even renaming EXT to something that's less.. relative :(  
> 
> Suggestion?

Well, is an SMT socket on the board an EXT pin?
Which is why I prefer PANEL.

> >> Well sure, in case there is no "label" attr for the rest of the types.
> >> Which I believe it is, for the ice implementation in this patchset.
> >> Otherwise, there is no way to distinguish between the pins.
> >> To have multiple attrs for label for multiple pin types does not make
> >> any sense to me, that was my point.  
> >
> >Come on, am I really this bad at explaining this?  
> 
> Or perhaps I'm just slow.
> 
> >If we make a generic "label" attribute driver authors will pack
> >everything they want to expose to the user into it, and then some.  
> 
> What's difference in generic label string attr and type specific label
> string attr. What is stopping driver developers to pack crap in either
> of these 2. Perhaps I'm missing something. Could you draw examples?
> 
> >So we need attributes which will feel *obviously* *wrong* to abuse.  
> 
> Sure, I get what you say and agree. I'm just trying to find out the
> actual attributes :)

PANEL label must match the name on the panel. User can take the card
into their hand, look at the front, and there should be a label/sticker/
/engraving which matches exactly what the kernel reports.

If the label is printed on the board it's a BOARD_LABEL, if it's the
name of a trace in board docs it's a BOARD_TRACE, if it's a pin of 
the ASIC it's a PACKAGE_PIN.

If it's none of those, or user does not have access to the detailed
board / pinout - don't use the label.

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

* Re: [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions
  2023-05-09 14:52                                                     ` Jakub Kicinski
@ 2023-05-09 15:21                                                       ` Jiri Pirko
  2023-05-09 17:53                                                         ` Jakub Kicinski
  0 siblings, 1 reply; 202+ messages in thread
From: Jiri Pirko @ 2023-05-09 15:21 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Paolo Abeni, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Vadim Fedorenko, Jonathan Lemon, poros, mschmidt, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

Tue, May 09, 2023 at 04:52:47PM CEST, kuba@kernel.org wrote:
>On Tue, 9 May 2023 09:53:07 +0200 Jiri Pirko wrote:
>> >Yup. Even renaming EXT to something that's less.. relative :(  
>> 
>> Suggestion?
>
>Well, is an SMT socket on the board an EXT pin?
>Which is why I prefer PANEL.

Makes sense.
To speak code, we'll have:

/**
 * 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_PANEL: physically facing user, for example on a front panel
 * @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_PANEL,
        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)
};


>
>> >> Well sure, in case there is no "label" attr for the rest of the types.
>> >> Which I believe it is, for the ice implementation in this patchset.
>> >> Otherwise, there is no way to distinguish between the pins.
>> >> To have multiple attrs for label for multiple pin types does not make
>> >> any sense to me, that was my point.  
>> >
>> >Come on, am I really this bad at explaining this?  
>> 
>> Or perhaps I'm just slow.
>> 
>> >If we make a generic "label" attribute driver authors will pack
>> >everything they want to expose to the user into it, and then some.  
>> 
>> What's difference in generic label string attr and type specific label
>> string attr. What is stopping driver developers to pack crap in either
>> of these 2. Perhaps I'm missing something. Could you draw examples?
>> 
>> >So we need attributes which will feel *obviously* *wrong* to abuse.  
>> 
>> Sure, I get what you say and agree. I'm just trying to find out the
>> actual attributes :)
>
>PANEL label must match the name on the panel. User can take the card
>into their hand, look at the front, and there should be a label/sticker/
>/engraving which matches exactly what the kernel reports.
>
>If the label is printed on the board it's a BOARD_LABEL, if it's the
>name of a trace in board docs it's a BOARD_TRACE, if it's a pin of 
>the ASIC it's a PACKAGE_PIN.
>
>If it's none of those, or user does not have access to the detailed
>board / pinout - don't use the label.

To speak code, we'll have:
DPLL_A_PIN_PANEL_LABEL (string)
   available always when attr[DPLL_A_PIN_TYPE] == DPLL_PIN_TYPE_PANEL
DPLL_A_PIN_BOARD_LABEL (string)
   may be available for any type, optional
DPLL_A_PIN_BOARD_TRACE (string)
   may be available for any type, optional
DPLL_A_PIN_PACKAGE_PIN (string)
   may be available for any type, optional

Makes sense?

But this does not prevent driver developer to pack random crap in the
string anyway :/

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

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

On Tue, 9 May 2023 17:21:42 +0200 Jiri Pirko wrote:
> Tue, May 09, 2023 at 04:52:47PM CEST, kuba@kernel.org wrote:
> >On Tue, 9 May 2023 09:53:07 +0200 Jiri Pirko wrote:  
> >> >Yup. Even renaming EXT to something that's less.. relative :(    
> >> 
> >> Suggestion?  
> >
> >Well, is an SMT socket on the board an EXT pin?
> >Which is why I prefer PANEL.  
> 
> Makes sense.
> To speak code, we'll have:
> 
> /**
>  * 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_PANEL: physically facing user, for example on a front panel
>  * @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_PANEL,
>         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)
> };

Maybe we can keep the EXT here, just not in the label itself.
Don't think we care to add pin type for PANEL vs SMT vs jumper?

> >> Sure, I get what you say and agree. I'm just trying to find out the
> >> actual attributes :)  
> >
> >PANEL label must match the name on the panel. User can take the card
> >into their hand, look at the front, and there should be a label/sticker/
> >/engraving which matches exactly what the kernel reports.
> >
> >If the label is printed on the board it's a BOARD_LABEL, if it's the
> >name of a trace in board docs it's a BOARD_TRACE, if it's a pin of 
> >the ASIC it's a PACKAGE_PIN.
> >
> >If it's none of those, or user does not have access to the detailed
> >board / pinout - don't use the label.  
> 
> To speak code, we'll have:
> DPLL_A_PIN_PANEL_LABEL (string)
>    available always when attr[DPLL_A_PIN_TYPE] == DPLL_PIN_TYPE_PANEL

Not sure about always, if there's only one maybe there's no need 
to provide the label?

> DPLL_A_PIN_BOARD_LABEL (string)
>    may be available for any type, optional
> DPLL_A_PIN_BOARD_TRACE (string)
>    may be available for any type, optional
> DPLL_A_PIN_PACKAGE_PIN (string)
>    may be available for any type, optional
> 
> Makes sense?

yup (obviously we need to document the semantics)

> But this does not prevent driver developer to pack random crap in the
> string anyway :/

It doesn't but it hopefully makes it much more likely that (1) reviewer
will notice that something is off if the driver printfs random crap;
and (2) that the user reading the documentation will complain that 
e.g.BOARD_LABEL is used but does not match the label on they see...

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

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

Tue, May 09, 2023 at 07:53:02PM CEST, kuba@kernel.org wrote:
>On Tue, 9 May 2023 17:21:42 +0200 Jiri Pirko wrote:
>> Tue, May 09, 2023 at 04:52:47PM CEST, kuba@kernel.org wrote:
>> >On Tue, 9 May 2023 09:53:07 +0200 Jiri Pirko wrote:  
>> >> >Yup. Even renaming EXT to something that's less.. relative :(    
>> >> 
>> >> Suggestion?  
>> >
>> >Well, is an SMT socket on the board an EXT pin?
>> >Which is why I prefer PANEL.  
>> 
>> Makes sense.
>> To speak code, we'll have:
>> 
>> /**
>>  * 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_PANEL: physically facing user, for example on a front panel
>>  * @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_PANEL,
>>         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)
>> };
>
>Maybe we can keep the EXT here, just not in the label itself.
>Don't think we care to add pin type for PANEL vs SMT vs jumper?

Okay:
 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)
};



>
>> >> Sure, I get what you say and agree. I'm just trying to find out the
>> >> actual attributes :)  
>> >
>> >PANEL label must match the name on the panel. User can take the card
>> >into their hand, look at the front, and there should be a label/sticker/
>> >/engraving which matches exactly what the kernel reports.
>> >
>> >If the label is printed on the board it's a BOARD_LABEL, if it's the
>> >name of a trace in board docs it's a BOARD_TRACE, if it's a pin of 
>> >the ASIC it's a PACKAGE_PIN.
>> >
>> >If it's none of those, or user does not have access to the detailed
>> >board / pinout - don't use the label.  
>> 
>> To speak code, we'll have:
>> DPLL_A_PIN_PANEL_LABEL (string)
>>    available always when attr[DPLL_A_PIN_TYPE] == DPLL_PIN_TYPE_PANEL
>
>Not sure about always, if there's only one maybe there's no need 
>to provide the label?

Well, I would like to have check in pin_register() for this. That would
be non-trivial if this would be required only in case pin_count>1.
However I can imagine a card with one connector without any label on it.
IDK.


>
>> DPLL_A_PIN_BOARD_LABEL (string)
>>    may be available for any type, optional
>> DPLL_A_PIN_BOARD_TRACE (string)
>>    may be available for any type, optional
>> DPLL_A_PIN_PACKAGE_PIN (string)
>>    may be available for any type, optional
>> 
>> Makes sense?
>
>yup (obviously we need to document the semantics)

Agreed.


>
>> But this does not prevent driver developer to pack random crap in the
>> string anyway :/
>
>It doesn't but it hopefully makes it much more likely that (1) reviewer
>will notice that something is off if the driver printfs random crap;
>and (2) that the user reading the documentation will complain that 
>e.g.BOARD_LABEL is used but does not match the label on they see...

Fair.

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

end of thread, other threads:[~2023-05-10  6:17 UTC | newest]

Thread overview: 202+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-12  2:28 [PATCH RFC v6 0/6] Create common DPLL/clock configuration API Vadim Fedorenko
2023-03-12  2:28 ` Vadim Fedorenko
2023-03-12  2:28 ` [PATCH RFC v6 1/6] dpll: spec: Add Netlink spec in YAML Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-14 14:44   ` Jiri Pirko
2023-03-14 14:44     ` Jiri Pirko
2023-03-16 13:15     ` Kubalewski, Arkadiusz
2023-03-16 13:15       ` Kubalewski, Arkadiusz
2023-03-16 13:45       ` Jiri Pirko
2023-03-16 13:45         ` Jiri Pirko
2023-03-16 15:19         ` Jiri Pirko
2023-03-16 15:19           ` Jiri Pirko
2023-03-17  0:53           ` Kubalewski, Arkadiusz
2023-03-17  0:53             ` Kubalewski, Arkadiusz
2023-03-17 10:07             ` Jiri Pirko
2023-03-17 10:07               ` Jiri Pirko
2023-03-17  0:52         ` Kubalewski, Arkadiusz
2023-03-17  0:52           ` Kubalewski, Arkadiusz
2023-03-17 10:05           ` Jiri Pirko
2023-03-17 10:05             ` Jiri Pirko
2023-03-17 14:29             ` Jiri Pirko
2023-03-17 14:29               ` Jiri Pirko
2023-03-17 15:14             ` Kubalewski, Arkadiusz
2023-03-17 15:14               ` Kubalewski, Arkadiusz
2023-03-17 16:20               ` Jiri Pirko
2023-03-17 16:20                 ` Jiri Pirko
2023-03-17 18:22                 ` Kubalewski, Arkadiusz
2023-03-17 18:22                   ` Kubalewski, Arkadiusz
2023-03-20  8:10                   ` Jiri Pirko
2023-03-20  8:10                     ` Jiri Pirko
2023-03-21  4:05       ` Jakub Kicinski
2023-03-21  4:05         ` Jakub Kicinski
2023-03-21  4:13         ` Jakub Kicinski
2023-03-21  4:13           ` Jakub Kicinski
2023-03-21  4:20           ` Jakub Kicinski
2023-03-21  4:20             ` Jakub Kicinski
2023-03-17 16:23   ` Jiri Pirko
2023-03-17 16:23     ` Jiri Pirko
2023-03-21  4:00     ` Jakub Kicinski
2023-03-21  4:00       ` Jakub Kicinski
2023-03-17 16:53   ` Jiri Pirko
2023-03-17 16:53     ` Jiri Pirko
2023-03-17 18:50     ` Kubalewski, Arkadiusz
2023-03-17 18:50       ` Kubalewski, Arkadiusz
2023-03-12  2:28 ` [PATCH RFC v6 2/6] dpll: Add DPLL framework base functions Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-13 16:21   ` Jiri Pirko
2023-03-13 16:21     ` Jiri Pirko
2023-03-13 22:59     ` Vadim Fedorenko
2023-03-13 22:59       ` Vadim Fedorenko
2023-03-14  9:21       ` Jiri Pirko
2023-03-14 17:50         ` Kubalewski, Arkadiusz
2023-03-14 17:50           ` Kubalewski, Arkadiusz
2023-03-15  9:22           ` Jiri Pirko
2023-03-15  9:22             ` Jiri Pirko
2023-03-16 12:31             ` Jiri Pirko
2023-03-16 12:31               ` Jiri Pirko
2023-03-28 15:22             ` Vadim Fedorenko
2023-03-28 15:22               ` Vadim Fedorenko
2023-04-01 12:49               ` Jiri Pirko
2023-04-01 12:49                 ` Jiri Pirko
2023-04-03 18:18             ` Jakub Kicinski
2023-04-03 18:18               ` Jakub Kicinski
2023-04-09  7:51               ` Jiri Pirko
2023-04-09  7:51                 ` Jiri Pirko
     [not found]                 ` <20230410153149.602c6bad@kernel.org>
2023-04-16 16:23                   ` Jiri Pirko
2023-04-16 16:23                     ` Jiri Pirko
2023-04-17 15:53                     ` Vadim Fedorenko
2023-04-17 15:53                       ` Vadim Fedorenko
     [not found]                     ` <20230417124942.4305abfa@kernel.org>
2023-04-27  8:05                       ` Paolo Abeni
2023-04-27  8:05                         ` Paolo Abeni
2023-04-27 10:20                         ` Vadim Fedorenko
2023-04-27 10:20                           ` Vadim Fedorenko
     [not found]                       ` <ZFDPaXlJainSOqmV@nanopsycho>
     [not found]                         ` <20230502083244.19543d26@kernel.org>
2023-05-03  7:56                           ` Jiri Pirko
2023-05-03  7:56                             ` Jiri Pirko
2023-05-04  2:16                             ` Jakub Kicinski
2023-05-04  2:16                               ` Jakub Kicinski
2023-05-04 11:00                               ` Jiri Pirko
2023-05-04 11:00                                 ` Jiri Pirko
2023-05-04 11:14                                 ` Jiri Pirko
2023-05-04 11:14                                   ` Jiri Pirko
2023-05-04 16:04                                 ` Jakub Kicinski
2023-05-04 16:04                                   ` Jakub Kicinski
2023-05-04 17:51                                   ` Jiri Pirko
2023-05-04 17:51                                     ` Jiri Pirko
2023-05-04 18:44                                     ` Jakub Kicinski
2023-05-04 18:44                                       ` Jakub Kicinski
2023-05-05 10:41                                       ` Jiri Pirko
2023-05-05 10:41                                         ` Jiri Pirko
2023-05-05 15:35                                         ` Jakub Kicinski
2023-05-05 15:35                                           ` Jakub Kicinski
2023-05-07  7:58                                           ` Jiri Pirko
2023-05-07  7:58                                             ` Jiri Pirko
2023-05-08  6:50                                             ` Paolo Abeni
2023-05-08 12:17                                               ` Jiri Pirko
2023-05-08 19:42                                                 ` Jakub Kicinski
2023-05-09  7:53                                                   ` Jiri Pirko
2023-05-09 14:52                                                     ` Jakub Kicinski
2023-05-09 15:21                                                       ` Jiri Pirko
2023-05-09 17:53                                                         ` Jakub Kicinski
2023-05-10  6:17                                                           ` Jiri Pirko
2023-03-14 16:43       ` Kubalewski, Arkadiusz
2023-03-14 16:43         ` Kubalewski, Arkadiusz
2023-03-15 12:14         ` Jiri Pirko
2023-03-15 12:14           ` Jiri Pirko
2023-03-14  9:30   ` Jiri Pirko
2023-03-14 15:45   ` Jiri Pirko
2023-03-14 15:45     ` Jiri Pirko
2023-03-14 18:35     ` Kubalewski, Arkadiusz
2023-03-14 18:35       ` Kubalewski, Arkadiusz
2023-03-15 14:43       ` Jiri Pirko
2023-03-15 14:43         ` Jiri Pirko
2023-03-15 15:29   ` Jiri Pirko
2023-03-15 15:29     ` Jiri Pirko
2023-03-16 12:20   ` Jiri Pirko
2023-03-16 12:20     ` Jiri Pirko
2023-03-16 12:37   ` Jiri Pirko
2023-03-16 12:37     ` Jiri Pirko
2023-03-16 13:53   ` Jiri Pirko
2023-03-16 13:53     ` Jiri Pirko
2023-03-16 16:16   ` Jiri Pirko
2023-03-16 16:16     ` Jiri Pirko
2023-03-17 16:21   ` Jiri Pirko
2023-03-17 16:21     ` Jiri Pirko
2023-03-20 10:24   ` Jiri Pirko
2023-03-20 10:24     ` Jiri Pirko
2023-03-21 13:34   ` Jiri Pirko
2023-03-21 13:34     ` Jiri Pirko
2023-03-23 11:18   ` Jiri Pirko
2023-03-23 11:18     ` Jiri Pirko
2023-03-24  9:29   ` Jiri Pirko
2023-03-24  9:29     ` Jiri Pirko
2023-03-12  2:28 ` [PATCH RFC v6 3/6] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-14 16:14   ` Jiri Pirko
2023-03-14 16:14     ` Jiri Pirko
2023-04-03 10:21     ` Kubalewski, Arkadiusz
2023-04-03 10:21       ` Kubalewski, Arkadiusz
2023-03-16 13:46   ` Jiri Pirko
2023-03-16 13:46     ` Jiri Pirko
2023-04-03 10:23     ` Kubalewski, Arkadiusz
2023-04-03 10:23       ` Kubalewski, Arkadiusz
2023-03-12  2:28 ` [PATCH RFC v6 4/6] ice: add admin commands to access cgu configuration Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-12  7:05   ` kernel test robot
2023-03-12  2:28 ` [PATCH RFC v6 5/6] ice: implement dpll interface to control cgu Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-12  6:04   ` kernel test robot
2023-03-12  2:28 ` [PATCH RFC v6 6/6] ptp_ocp: implement DPLL ops Vadim Fedorenko
2023-03-12  2:28   ` Vadim Fedorenko
2023-03-12  5:12   ` kernel test robot
2023-03-14 10:05   ` Jiri Pirko
2023-03-15  0:10     ` Vadim Fedorenko
2023-03-15  0:10       ` Vadim Fedorenko
2023-03-15 12:24       ` Jiri Pirko
2023-03-15 12:24         ` Jiri Pirko
2023-03-31 23:28         ` Vadim Fedorenko
2023-03-31 23:28           ` Vadim Fedorenko
2023-04-01 12:53           ` Jiri Pirko
2023-04-01 12:53             ` Jiri Pirko
2023-03-15 15:34   ` Jiri Pirko
2023-03-15 15:34     ` Jiri Pirko
2023-03-15 15:52     ` Vadim Fedorenko
2023-03-15 15:52       ` Vadim Fedorenko
2023-03-16 12:12   ` Jiri Pirko
2023-03-16 12:12     ` Jiri Pirko
2023-03-13 12:20 ` [PATCH RFC v6 0/6] Create common DPLL/clock configuration API Jiri Pirko
2023-03-13 12:20   ` Jiri Pirko
2023-03-13 15:33   ` Vadim Fedorenko
2023-03-13 15:33     ` Vadim Fedorenko
2023-03-13 16:22     ` Jiri Pirko
2023-03-13 16:22       ` Jiri Pirko
2023-03-13 16:31       ` Vadim Fedorenko
2023-03-13 16:31         ` Vadim Fedorenko
2023-03-17 16:10     ` Jiri Pirko
2023-03-17 16:10       ` Jiri Pirko
2023-03-18  5:01       ` Jakub Kicinski
2023-03-18  5:01         ` Jakub Kicinski
2023-03-23 11:21 ` Jiri Pirko
2023-03-23 11:21   ` Jiri Pirko
2023-03-23 18:00   ` Vadim Fedorenko
2023-03-23 18:00     ` Vadim Fedorenko
2023-03-26 17:00 ` [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation Jiri Pirko
2023-03-26 17:00   ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 1/7] dpll: make ops function args const Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 2/7] dpll: allow to call device register multiple times Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 3/7] dpll: introduce a helper to get first dpll ref and use it Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 4/7] dpll: allow to call pin register multiple times Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 5/7] dpll: export dpll_pin_notify() Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 6/7] netdev: expose DPLL pin handle for netdevice Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-26 17:00   ` [patch dpll-rfc 7/7] mlx5: Implement SyncE support using DPLL infrastructure Jiri Pirko
2023-03-26 17:00     ` Jiri Pirko
2023-03-28 16:36   ` [patch dpll-rfc 0/7] dpll: initial patchset extension by mlx5 implementation Vadim Fedorenko
2023-03-28 16:36     ` Vadim Fedorenko
2023-04-01 12:54     ` Jiri Pirko
2023-04-01 12:54       ` Jiri Pirko

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.