All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-20  9:18 ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

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

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

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

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

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

v8 RFC -> v0:
- Merge header patch into the patches where the actual functions are
  implemented
- Address comments from previous reviews
- Per patch change log contains more details

v8 -> v9:
[00/10] Create common DPLL configuration API
- update examples to reflect new pin-parent nest split

[01/10] dpll: documentation on DPLL subsystem interface
- fix docs build warnings
- separate netlink command/attribute list
- replace enum description with uapi header
- add brief explanation what is a DPLL
- fix EOPNOTSUPP typo
- fix typo .state_get -> .state_on_dpll_get

[02/10] dpll: spec: Add Netlink spec in YAML
- regenerate policy max values
- add missing enum descriptions
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple
- fix typos:
  - s/working-modes/working modes/
  - s/differentiate/differentiates/
  - s/valid input, auto selected by dpll/input pin auto selected by dpll/
- remove FREERUN and HOLDOVER modes

[03/10] dpll: core: Add DPLL framework base functions
- fix description in spdx header.
- remove refcount check if refcount was already set
- do not validate dpll ptr in dpll_device_put(..)
- fix return -ENOMEM on failed memory alloc
- do not validate pin ptr in dpll_pin_put(..)
- return -EINVAL in case of module/clock_id mismatch
- do not {} around one-line xa_for_each() macro
- move dpll_<x>_registration structs to dpll_core.c
- rephrase doc comment on device and pin id struct members
- remove ref in case of memory allocation fail
- check for required ops on pin/device registration
- mark pin with DPLL_REGISTERED once pin is registered with dpll

[04/10] dpll: netlink: Add DPLL framework base functions
- fix pin-id-get/device-id-get behavior
- reshuffle order of functions
- avoid forward declarations
- functions for adding pin/device handle next to each other
- pass ops callback return values to the user
- remove dpll_cmd_pin_fill_details(..) function, merge the code into
  __dpll_cmd_pin_dump_one(..)
- rename __dpll_cmd_pin_dump_one() to dpll_cmd_pin_get_one()
- use WARN_ON macro when dpll ref is missing
- remove redundant pin's dpll list not empty check
- remove double spaces inside if statement
- add extack message when set command is not possible
- do not return error when callback is not required
- WARN_ON missing ops moved to dpll_core.c
- use DPLL_REGISTERED if pin was registered with dpll
- fix pin-id-get return and add extack errors
- fix device-id-get return and add extack errors
- drop pointless init of variables
- add macro for iterating over marked pins/devices
- move dpll_set_from_nlattr() for consistent order
- use GENL_REQ_ATTR_CHECK() for checking attibute presence
- fill extack if pin/device was not found
- drop pointless init of variables
- WARN_ON if dpll not registered on send event
- rename goto labels to indicate error path
- fix docs
- drop pointless init of variables
- verify pin in notify with a mark
- prevent ops->mode_set call if missing callback
- move static dpll_msg_add_pin_handle() from pin<->netdev patch
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple

[06/10] netdev: expose DPLL pin handle for netdevice
- net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
  code in net/core/rtnetlink.c to respect that.
- move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
  function with this patch

[07/10] ice: add admin commands to access cgu configuration
- rename MAX_NETLIST_SIZE -> ICE_MAX_NETLIST_SIZE
- simplify function: s64 convert_s48_to_s64(s64 signed_48)
- do not assign 0 to field that is already 0

[08/10] ice: implement dpll interface to control cgu
- drop pointless 0 assignement
- ice_dpll_init(..) returns void instead of int
- fix context description of the functions
- fix ice_dpll_init(..) traces
- fix use package_label instead pf board_label for rclk pin
- be consistent on cgu presence naming
- remove indent in ice_dpll_deinit(..)
- remove unused struct field lock_err_num
- fix kworker resched behavior
- remove debug log from ice_dpll_deinit_worker(..)
- reorder ice internal functions
- release resources directly on error path
- remove redundant NULL checks when releasing resources
- do not assign NULL to pointers after releasing resources
- simplify variable assignement
- fix 'int ret;' declarations across the ice_dpll.c
- remove leftover ice_dpll_find(..)
- get pf pointer from dpll_priv without type cast
- improve error reporting
- fix documentation
- fix ice_dpll_update_state(..) flow
- fix return in case out of range prio set


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

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

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

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

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

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

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

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

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

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

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

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

Vadim Fedorenko (5):
  dpll: documentation on DPLL subsystem interface
  dpll: core: Add DPLL framework base functions
  dpll: netlink: Add DPLL framework base functions
  dpll: api header: Add DPLL framework base functions
  ptp_ocp: implement DPLL ops
Arkadiusz Kubalewski (4):
  tools: ynl-gen: fix enum index in _decode_enum(..)
  tools: ynl-gen: fix parse multi-attr enum attribute
  ice: add admin commands to access cgu configuration
  ice: implement dpll interface to control cgu

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

Vadim Fedorenko (5):
  dpll: documentation on DPLL subsystem interface
  dpll: spec: Add Netlink spec in YAML
  dpll: core: Add DPLL framework base functions
  dpll: netlink: Add DPLL framework base functions
  ptp_ocp: implement DPLL ops

 Documentation/driver-api/dpll.rst             |  431 ++++
 Documentation/driver-api/index.rst            |    1 +
 Documentation/netlink/specs/dpll.yaml         |  475 ++++
 MAINTAINERS                                   |   11 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |    9 +
 drivers/dpll/dpll_core.c                      |  799 +++++++
 drivers/dpll/dpll_core.h                      |   90 +
 drivers/dpll/dpll_netlink.c                   | 1286 +++++++++++
 drivers/dpll/dpll_netlink.h                   |   17 +
 drivers/dpll/dpll_nl.c                        |  163 ++
 drivers/dpll/dpll_nl.h                        |   51 +
 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   |  245 +-
 drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
 drivers/net/ethernet/intel/ice/ice_common.h   |   44 +
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 2053 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |  104 +
 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   |  419 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  231 ++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 .../net/ethernet/mellanox/mlx5/core/Kconfig   |    8 +
 .../net/ethernet/mellanox/mlx5/core/Makefile  |    3 +
 drivers/net/ethernet/mellanox/mlx5/core/dev.c |   17 +
 .../net/ethernet/mellanox/mlx5/core/dpll.c    |  432 ++++
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  366 ++-
 include/linux/dpll.h                          |  160 ++
 include/linux/mlx5/driver.h                   |    2 +
 include/linux/mlx5/mlx5_ifc.h                 |   59 +-
 include/linux/netdevice.h                     |   20 +
 include/uapi/linux/dpll.h                     |  186 ++
 include/uapi/linux/if_link.h                  |    2 +
 net/core/dev.c                                |   22 +
 net/core/rtnetlink.c                          |   35 +
 tools/net/ynl/lib/ynl.py                      |   18 +-
 42 files changed, 8194 insertions(+), 77 deletions(-)
 create mode 100644 Documentation/driver-api/dpll.rst
 create mode 100644 Documentation/netlink/specs/dpll.yaml
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 drivers/dpll/dpll_nl.c
 create mode 100644 drivers/dpll/dpll_nl.h
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.27.0


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

* [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-20  9:18 ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

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

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

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

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

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

v8 RFC -> v0:
- Merge header patch into the patches where the actual functions are
  implemented
- Address comments from previous reviews
- Per patch change log contains more details

v8 -> v9:
[00/10] Create common DPLL configuration API
- update examples to reflect new pin-parent nest split

[01/10] dpll: documentation on DPLL subsystem interface
- fix docs build warnings
- separate netlink command/attribute list
- replace enum description with uapi header
- add brief explanation what is a DPLL
- fix EOPNOTSUPP typo
- fix typo .state_get -> .state_on_dpll_get

[02/10] dpll: spec: Add Netlink spec in YAML
- regenerate policy max values
- add missing enum descriptions
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple
- fix typos:
  - s/working-modes/working modes/
  - s/differentiate/differentiates/
  - s/valid input, auto selected by dpll/input pin auto selected by dpll/
- remove FREERUN and HOLDOVER modes

[03/10] dpll: core: Add DPLL framework base functions
- fix description in spdx header.
- remove refcount check if refcount was already set
- do not validate dpll ptr in dpll_device_put(..)
- fix return -ENOMEM on failed memory alloc
- do not validate pin ptr in dpll_pin_put(..)
- return -EINVAL in case of module/clock_id mismatch
- do not {} around one-line xa_for_each() macro
- move dpll_<x>_registration structs to dpll_core.c
- rephrase doc comment on device and pin id struct members
- remove ref in case of memory allocation fail
- check for required ops on pin/device registration
- mark pin with DPLL_REGISTERED once pin is registered with dpll

[04/10] dpll: netlink: Add DPLL framework base functions
- fix pin-id-get/device-id-get behavior
- reshuffle order of functions
- avoid forward declarations
- functions for adding pin/device handle next to each other
- pass ops callback return values to the user
- remove dpll_cmd_pin_fill_details(..) function, merge the code into
  __dpll_cmd_pin_dump_one(..)
- rename __dpll_cmd_pin_dump_one() to dpll_cmd_pin_get_one()
- use WARN_ON macro when dpll ref is missing
- remove redundant pin's dpll list not empty check
- remove double spaces inside if statement
- add extack message when set command is not possible
- do not return error when callback is not required
- WARN_ON missing ops moved to dpll_core.c
- use DPLL_REGISTERED if pin was registered with dpll
- fix pin-id-get return and add extack errors
- fix device-id-get return and add extack errors
- drop pointless init of variables
- add macro for iterating over marked pins/devices
- move dpll_set_from_nlattr() for consistent order
- use GENL_REQ_ATTR_CHECK() for checking attibute presence
- fill extack if pin/device was not found
- drop pointless init of variables
- WARN_ON if dpll not registered on send event
- rename goto labels to indicate error path
- fix docs
- drop pointless init of variables
- verify pin in notify with a mark
- prevent ops->mode_set call if missing callback
- move static dpll_msg_add_pin_handle() from pin<->netdev patch
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple

[06/10] netdev: expose DPLL pin handle for netdevice
- net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
  code in net/core/rtnetlink.c to respect that.
- move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
  function with this patch

[07/10] ice: add admin commands to access cgu configuration
- rename MAX_NETLIST_SIZE -> ICE_MAX_NETLIST_SIZE
- simplify function: s64 convert_s48_to_s64(s64 signed_48)
- do not assign 0 to field that is already 0

[08/10] ice: implement dpll interface to control cgu
- drop pointless 0 assignement
- ice_dpll_init(..) returns void instead of int
- fix context description of the functions
- fix ice_dpll_init(..) traces
- fix use package_label instead pf board_label for rclk pin
- be consistent on cgu presence naming
- remove indent in ice_dpll_deinit(..)
- remove unused struct field lock_err_num
- fix kworker resched behavior
- remove debug log from ice_dpll_deinit_worker(..)
- reorder ice internal functions
- release resources directly on error path
- remove redundant NULL checks when releasing resources
- do not assign NULL to pointers after releasing resources
- simplify variable assignement
- fix 'int ret;' declarations across the ice_dpll.c
- remove leftover ice_dpll_find(..)
- get pf pointer from dpll_priv without type cast
- improve error reporting
- fix documentation
- fix ice_dpll_update_state(..) flow
- fix return in case out of range prio set


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

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

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

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

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

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

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

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

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

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

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

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

Vadim Fedorenko (5):
  dpll: documentation on DPLL subsystem interface
  dpll: core: Add DPLL framework base functions
  dpll: netlink: Add DPLL framework base functions
  dpll: api header: Add DPLL framework base functions
  ptp_ocp: implement DPLL ops
Arkadiusz Kubalewski (4):
  tools: ynl-gen: fix enum index in _decode_enum(..)
  tools: ynl-gen: fix parse multi-attr enum attribute
  ice: add admin commands to access cgu configuration
  ice: implement dpll interface to control cgu

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

Vadim Fedorenko (5):
  dpll: documentation on DPLL subsystem interface
  dpll: spec: Add Netlink spec in YAML
  dpll: core: Add DPLL framework base functions
  dpll: netlink: Add DPLL framework base functions
  ptp_ocp: implement DPLL ops

 Documentation/driver-api/dpll.rst             |  431 ++++
 Documentation/driver-api/index.rst            |    1 +
 Documentation/netlink/specs/dpll.yaml         |  475 ++++
 MAINTAINERS                                   |   11 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |    9 +
 drivers/dpll/dpll_core.c                      |  799 +++++++
 drivers/dpll/dpll_core.h                      |   90 +
 drivers/dpll/dpll_netlink.c                   | 1286 +++++++++++
 drivers/dpll/dpll_netlink.h                   |   17 +
 drivers/dpll/dpll_nl.c                        |  163 ++
 drivers/dpll/dpll_nl.h                        |   51 +
 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   |  245 +-
 drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
 drivers/net/ethernet/intel/ice/ice_common.h   |   44 +
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 2053 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |  104 +
 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   |  419 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  231 ++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 .../net/ethernet/mellanox/mlx5/core/Kconfig   |    8 +
 .../net/ethernet/mellanox/mlx5/core/Makefile  |    3 +
 drivers/net/ethernet/mellanox/mlx5/core/dev.c |   17 +
 .../net/ethernet/mellanox/mlx5/core/dpll.c    |  432 ++++
 drivers/ptp/Kconfig                           |    1 +
 drivers/ptp/ptp_ocp.c                         |  366 ++-
 include/linux/dpll.h                          |  160 ++
 include/linux/mlx5/driver.h                   |    2 +
 include/linux/mlx5/mlx5_ifc.h                 |   59 +-
 include/linux/netdevice.h                     |   20 +
 include/uapi/linux/dpll.h                     |  186 ++
 include/uapi/linux/if_link.h                  |    2 +
 net/core/dev.c                                |   22 +
 net/core/rtnetlink.c                          |   35 +
 tools/net/ynl/lib/ynl.py                      |   18 +-
 42 files changed, 8194 insertions(+), 77 deletions(-)
 create mode 100644 Documentation/driver-api/dpll.rst
 create mode 100644 Documentation/netlink/specs/dpll.yaml
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 drivers/dpll/dpll_nl.c
 create mode 100644 drivers/dpll/dpll_nl.h
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

Remove wrong index adjustement, which is leftover from adding
support for sparse enums.
enum.entries_by_val() function shall not subtract the start-value, as
it is indexed with real enum value.

Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic CLI")
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 tools/net/ynl/lib/ynl.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 1b3a36fbb1c3..3908438d3716 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -420,16 +420,14 @@ class YnlFamily(SpecFamily):
     def _decode_enum(self, rsp, attr_spec):
         raw = rsp[attr_spec['name']]
         enum = self.consts[attr_spec['enum']]
-        i = attr_spec.get('value-start', 0)
         if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
             value = set()
             while raw:
                 if raw & 1:
-                    value.add(enum.entries_by_val[i].name)
+                    value.add(enum.entries_by_val[raw & 1].name)
                 raw >>= 1
-                i += 1
         else:
-            value = enum.entries_by_val[raw - i].name
+            value = enum.entries_by_val[raw].name
         rsp[attr_spec['name']] = value
 
     def _decode_binary(self, attr, attr_spec):
-- 
2.27.0


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

* [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

Remove wrong index adjustement, which is leftover from adding
support for sparse enums.
enum.entries_by_val() function shall not subtract the start-value, as
it is indexed with real enum value.

Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic CLI")
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 tools/net/ynl/lib/ynl.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 1b3a36fbb1c3..3908438d3716 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -420,16 +420,14 @@ class YnlFamily(SpecFamily):
     def _decode_enum(self, rsp, attr_spec):
         raw = rsp[attr_spec['name']]
         enum = self.consts[attr_spec['enum']]
-        i = attr_spec.get('value-start', 0)
         if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
             value = set()
             while raw:
                 if raw & 1:
-                    value.add(enum.entries_by_val[i].name)
+                    value.add(enum.entries_by_val[raw & 1].name)
                 raw >>= 1
-                i += 1
         else:
-            value = enum.entries_by_val[raw - i].name
+            value = enum.entries_by_val[raw].name
         rsp[attr_spec['name']] = value
 
     def _decode_binary(self, attr, attr_spec):
-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 02/11] tools: ynl-gen: fix parse multi-attr enum attribute
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

When attribute is enum type and marked as multi-attr, the netlink
respond is not parsed, fails with stack trace:
Traceback (most recent call last):
  File "/net-next/tools/net/ynl/./test.py", line 520, in <module>
    main()
  File "/net-next/tools/net/ynl/./test.py", line 488, in main
    dplls=dplls_get(282574471561216)
  File "/net-next/tools/net/ynl/./test.py", line 48, in dplls_get
    reply=act(args)
  File "/net-next/tools/net/ynl/./test.py", line 41, in act
    reply = ynl.dump(args.dump, attrs)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 598, in dump
    return self._op(method, vals, dump=True)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 584, in _op
    rsp_msg = self._decode(gm.raw_attrs, op.attr_set.name)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 451, in _decode
    self._decode_enum(rsp, attr_spec)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 408, in _decode_enum
    value = enum.entries_by_val[raw].name
TypeError: unhashable type: 'list'
error: 1

Redesign _decode_enum(..) to take a enum int value and translate
it to either a bitmask or enum name as expected.

Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 tools/net/ynl/lib/ynl.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 3908438d3716..06d88f083f95 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -417,8 +417,7 @@ class YnlFamily(SpecFamily):
         pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4)
         return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad
 
-    def _decode_enum(self, rsp, attr_spec):
-        raw = rsp[attr_spec['name']]
+    def _decode_enum(self, raw, attr_spec):
         enum = self.consts[attr_spec['enum']]
         if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
             value = set()
@@ -428,7 +427,7 @@ class YnlFamily(SpecFamily):
                 raw >>= 1
         else:
             value = enum.entries_by_val[raw].name
-        rsp[attr_spec['name']] = value
+        return value
 
     def _decode_binary(self, attr, attr_spec):
         if attr_spec.struct_name:
@@ -436,7 +435,7 @@ class YnlFamily(SpecFamily):
             decoded = attr.as_struct(members)
             for m in members:
                 if m.enum:
-                    self._decode_enum(decoded, m)
+                    decoded[m] = self._decode_enum(decoded[m], m)
         elif attr_spec.sub_type:
             decoded = attr.as_c_array(attr_spec.sub_type)
         else:
@@ -464,6 +463,9 @@ class YnlFamily(SpecFamily):
             else:
                 raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
 
+            if 'enum' in attr_spec:
+                decoded = self._decode_enum(int.from_bytes(attr.raw, "big"), attr_spec)
+
             if not attr_spec.is_multi:
                 rsp[attr_spec['name']] = decoded
             elif attr_spec.name in rsp:
@@ -471,8 +473,6 @@ class YnlFamily(SpecFamily):
             else:
                 rsp[attr_spec.name] = [decoded]
 
-            if 'enum' in attr_spec:
-                self._decode_enum(rsp, attr_spec)
         return rsp
 
     def _decode_extack_path(self, attrs, attr_set, offset, target):
-- 
2.27.0


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

* [PATCH net-next 02/11] tools: ynl-gen: fix parse multi-attr enum attribute
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

When attribute is enum type and marked as multi-attr, the netlink
respond is not parsed, fails with stack trace:
Traceback (most recent call last):
  File "/net-next/tools/net/ynl/./test.py", line 520, in <module>
    main()
  File "/net-next/tools/net/ynl/./test.py", line 488, in main
    dplls=dplls_get(282574471561216)
  File "/net-next/tools/net/ynl/./test.py", line 48, in dplls_get
    reply=act(args)
  File "/net-next/tools/net/ynl/./test.py", line 41, in act
    reply = ynl.dump(args.dump, attrs)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 598, in dump
    return self._op(method, vals, dump=True)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 584, in _op
    rsp_msg = self._decode(gm.raw_attrs, op.attr_set.name)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 451, in _decode
    self._decode_enum(rsp, attr_spec)
  File "/net-next/tools/net/ynl/lib/ynl.py", line 408, in _decode_enum
    value = enum.entries_by_val[raw].name
TypeError: unhashable type: 'list'
error: 1

Redesign _decode_enum(..) to take a enum int value and translate
it to either a bitmask or enum name as expected.

Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 tools/net/ynl/lib/ynl.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 3908438d3716..06d88f083f95 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -417,8 +417,7 @@ class YnlFamily(SpecFamily):
         pad = b'\x00' * ((4 - len(attr_payload) % 4) % 4)
         return struct.pack('HH', len(attr_payload) + 4, nl_type) + attr_payload + pad
 
-    def _decode_enum(self, rsp, attr_spec):
-        raw = rsp[attr_spec['name']]
+    def _decode_enum(self, raw, attr_spec):
         enum = self.consts[attr_spec['enum']]
         if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
             value = set()
@@ -428,7 +427,7 @@ class YnlFamily(SpecFamily):
                 raw >>= 1
         else:
             value = enum.entries_by_val[raw].name
-        rsp[attr_spec['name']] = value
+        return value
 
     def _decode_binary(self, attr, attr_spec):
         if attr_spec.struct_name:
@@ -436,7 +435,7 @@ class YnlFamily(SpecFamily):
             decoded = attr.as_struct(members)
             for m in members:
                 if m.enum:
-                    self._decode_enum(decoded, m)
+                    decoded[m] = self._decode_enum(decoded[m], m)
         elif attr_spec.sub_type:
             decoded = attr.as_c_array(attr_spec.sub_type)
         else:
@@ -464,6 +463,9 @@ class YnlFamily(SpecFamily):
             else:
                 raise Exception(f'Unknown {attr_spec["type"]} with name {attr_spec["name"]}')
 
+            if 'enum' in attr_spec:
+                decoded = self._decode_enum(int.from_bytes(attr.raw, "big"), attr_spec)
+
             if not attr_spec.is_multi:
                 rsp[attr_spec['name']] = decoded
             elif attr_spec.name in rsp:
@@ -471,8 +473,6 @@ class YnlFamily(SpecFamily):
             else:
                 rsp[attr_spec.name] = [decoded]
 
-            if 'enum' in attr_spec:
-                self._decode_enum(rsp, attr_spec)
         return rsp
 
     def _decode_extack_path(self, attrs, attr_set, offset, target):
-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 03/11] dpll: documentation on DPLL subsystem interface
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche,
	Bagas Sanjaya

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.

Co-developed-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- fix muxed get-pin command respond example
- fix typos
- describe DPLL_MODE_FREERUN

v8->v9:
- fix docs build warnings
- separate netlink command/attribute list
- replace enum description with uapi header
- add brief explanation what is a DPLL
- fix EOPNOTSUPP typo
- fix typo .state_get -> .state_on_dpll_get

 Documentation/driver-api/dpll.rst  | 431 +++++++++++++++++++++++++++++
 Documentation/driver-api/index.rst |   1 +
 2 files changed, 432 insertions(+)
 create mode 100644 Documentation/driver-api/dpll.rst

diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
new file mode 100644
index 000000000000..c11dcb74d11e
--- /dev/null
+++ b/Documentation/driver-api/dpll.rst
@@ -0,0 +1,431 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux kernel dpll subsystem
+===============================
+
+DPLL
+====
+
+PLL - Phase Locked Loop is an electronic circuit which syntonizes clock
+signal of a device with an external clock signal. Effectively enabling
+device to run on the same clock signal beat as provided on a PLL input.
+
+DPLL - Digital Phase Locked Loop is am integrated circuit which in
+addition to plain PLL behavior incorporates a digital phase detector
+and may have digital divider in the loop. As a result, the frequency on
+DPLL's input and output may be configurable.
+
+Subsystem
+=========
+
+The main purpose of dpll subsystem is to provide general interface
+to configure devices that use any kind of Digital PLL and could use
+different sources of input signal to synchronize to, as well as
+different types of outputs.
+The main interface is NETLINK_GENERIC based protocol with an event
+monitoring multicast group defined.
+
+Device object
+=============
+
+Single dpll device object means single Digital PLL circuit and bunch of
+connected pins.
+It reports the supported modes of operation and current status to the
+user in response to the `do` request of netlink command
+``DPLL_CMD_DEVICE_GET`` and list of dplls registered in the subsystem
+with `dump` netlink request of the same command.
+Changing the configuration of dpll device is done with `do` request of
+netlink ``DPLL_CMD_DEVICE_SET`` command.
+A device handle is ``DPLL_A_ID``, it shall be provided to get or set
+configuration of particular device in the system. It can be obtained
+with a ``DPLL_CMD_DEVICE_GET`` `dump` request or
+a ``DPLL_CMD_DEVICE_ID_GET`` `do` request, where the one must provide
+attributes that result in single device match.
+
+Pin object
+==========
+
+A pin is amorphic object which represents either input or output, it
+could be internal component of the device, as well as externally
+connected.
+The number of pins per dpll vary, but usually multiple pins shall be
+provided for a single dpll device.
+Pin's properties, capabilities and status is provided to the user in
+response to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
+It is also possible to list all the pins that were registered in the
+system with `dump` request of ``DPLL_CMD_PIN_GET`` command.
+Configuration of a pin can be changed by `do` request of netlink
+``DPLL_CMD_PIN_SET`` command.
+Pin handle is a ``DPLL_A_PIN_ID``, it shall be provided to get or set
+configuration of particular pin in the system. It can be obtained with
+``DPLL_CMD_PIN_GET`` `dump` request or ``DPLL_CMD_PIN_ID_GET`` `do`
+request, where user provides attributes that result in single pin match.
+
+Pin selection
+=============
+
+In general, selected pin (the one which signal is driving the dpll
+device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
+one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
+device.
+
+Pin selection can be done either manually or automatically, depending
+on hardware capabilities and active dpll device work mode
+(``DPLL_A_MODE`` attribute). The consequence is that there are
+differences for each mode in terms of available pin states, as well as
+for the states the user can request for a dpll device.
+
+In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
+one of following pin states:
+
+- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
+  device
+
+In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
+receive one of following pin states:
+
+- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
+  input for automatic selection algorithm
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
+  a valid input for automatic selection algorithm
+
+In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
+pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
+algorithm locks a dpll device with one of the inputs.
+
+For other dpll device operating modes there is no pin selection
+mechanics.
+
+Shared pins
+===========
+
+A single pin object can be attached to multiple dpll devices.
+Then there are two groups of configuration knobs:
+
+1) Set on a pin - the configuration affects all dpll devices pin is
+   registered to (i.e. ``DPLL_A_PIN_FREQUENCY``),
+2) Set on a pin-dpll tuple - the configuration affects only selected
+   dpll device (i.e. ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
+   ``DPLL_A_PIN_DIRECTION``).
+
+MUX-type pins
+=============
+
+A pin can be MUX-type, it aggregates child pins and serves as a pin
+multiplexer. One or more pins are registered with MUX-type instead of
+being directly registered to a dpll device.
+Pins registered with a MUX-type pin provide user with additional nested
+attribute ``DPLL_A_PIN_PARENT`` for each parent they were registered
+with.
+If a pin was registered with multiple parent pins, they behave like a
+multiple output multiplexer. In this case output of a
+``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
+attributes with current state related to each parent, like::
+
+  'pin': [{
+   {'clock-id': 282574471561216,
+    'module-name': 'ice',
+    'pin-dpll-caps': 4,
+    'pin-id': 13,
+    'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
+                   {'pin-id': 3, 'pin-state': 'disconnected'}],
+    'pin-type': 'synce-eth-port'}
+  }]
+
+Only one child pin can provide its signal to the parent MUX-type pin at
+a time, the selection is done by requesting change of a child pin state
+on desired parent, with the use of ``DPLL_A_PIN_PARENT`` nested
+attribute. Example of netlink `set state on parent pin` message format:
+
+  ====================== =============================================
+  ``DPLL_A_PIN_ID``      child pin id
+  ``DPLL_A_PIN_PARENT``  nested attribute for requesting configuration
+                         related to parent pin
+    ``DPLL_A_PIN_ID``    parent pin id
+    ``DPLL_A_PIN_STATE`` requested pin state on parent
+  ====================== =============================================
+
+Pin priority
+============
+
+Some devices might offer a capability of automatic pin selection mode
+(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
+Usually, automatic selection is performed on the hardware level, which
+means only pins directly connected to the dpll can be used for automatic
+input pin selection.
+In automatic selection mode, the user cannot manually select a input
+pin for the device, instead the user shall provide all directly
+connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
+pick a highest priority valid signal and use it to control the DPLL
+device. Example of netlink `set priority on parent pin` message format:
+
+  =====================  =============================================
+  ``DPLL_A_PIN_ID``      child pin id
+  ``DPLL_A_PIN_PARENT``  nested attribute for requesting configuration
+                         related to parent pin
+    ``DPLL_A_ID``        parent dpll id
+    ``DPLL_A_PIN_PRIO``  requested pin prio on parent dpll
+  =====================  =============================================
+
+Child pin of MUX-type is not capable of automatic input pin selection,
+in order to configure a input of a MUX-type pin, the user needs to
+request desired pin state of the child pin on the parent pin,
+as described in the ``MUX-type pins`` chapter.
+
+Configuration commands group
+============================
+
+Configuration commands are used to get information about registered
+dpll devices (and pins), as well as set configuration of device or pins.
+As dpll devices must be abstracted and reflect real hardware,
+there is no way to add new dpll device via netlink from user space and
+each device should be registered by its driver.
+
+All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
+any spamming/DoS from unauthorized userspace applications.
+
+List of netlink commands with possible attributes
+=================================================
+
+All constants identifying command types use a ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use a ``DPLL_A_``
+prefix and suffix according to attribute purpose:
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_ID_GET``           command to get device ID
+  ``DPLL_A_MODULE_NAME``               attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_TYPE``                    attr type of dpll device
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_GET``              command to get device info or
+                                       dump list of available devices
+    ``DPLL_A_ID``                      attr unique dpll device ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_MODE``                    attr selection mode
+    ``DPLL_A_MODE_SUPPORTED``          attr available selection modes
+    ``DPLL_A_LOCK_STATUS``             attr dpll device lock status
+    ``DPLL_A_TEMP``                    attr device temperature info
+    ``DPLL_A_TYPE``                    attr type of dpll device
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_SET``              command to set dpll device config
+    ``DPLL_A_ID``                      attr internal dpll device index
+    ``DPLL_A_MODE``                    attr selection mode to configure
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_ID_GET``              command to get pin ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_PIN_BOARD_LABEL``         attr pin board label provided
+                                       by registerer
+    ``DPLL_A_PIN_PANEL_LABEL``         attr pin panel label provided
+                                       by registerer
+    ``DPLL_A_PIN_PACKAGE_LABEL``       attr pin package label provided
+                                       by registerer
+    ``DPLL_A_PIN_TYPE``                attr type of a pin
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_GET``                 command to get pin info or dump
+                                       list of available pins
+    ``DPLL_A_PIN_ID``                  attr unique a pin ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_PIN_BOARD_LABEL``         attr pin board label provided
+                                       by registerer
+    ``DPLL_A_PIN_PANEL_LABEL``         attr pin panel label provided
+                                       by registerer
+    ``DPLL_A_PIN_PACKAGE_LABEL``       attr pin package label provided
+                                       by registerer
+    ``DPLL_A_PIN_TYPE``                attr type of a pin
+    ``DPLL_A_PIN_DIRECTION``           attr direction of a pin
+    ``DPLL_A_PIN_FREQUENCY``           attr current frequency of a pin
+    ``DPLL_A_PIN_FREQUENCY_SUPPORTED`` nested attr provides supported
+                                       frequencies
+      ``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
+      ``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
+    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent device
+                                       the pin is connected with
+      ``DPLL_A_ID``                    attr provided if parent is dpll
+                                       device
+      ``DPLL_A_PIN_PRIO``              attr priority of pin on the
+                                       dpll device
+      ``DPLL_A_PIN_STATE``             attr state of pin on the parent
+                                       dpll device
+    ``DPLL_A_PIN_PARENT_PIN``          nested attr for each parent pin
+                                       the pin is connected with
+      ``DPLL_A_PIN_ID``                attr provided if parent is a pin
+      ``DPLL_A_PIN_STATE``             attr state of pin on the parent
+                                       pin
+    ``DPLL_A_PIN_DPLL_CAPS``           attr bitmask of pin-dpll
+                                       capabilities
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_SET``                 command to set pins configuration
+    ``DPLL_A_PIN_ID``                  attr unique a pin ID
+    ``DPLL_A_PIN_DIRECTION``           attr requested direction of a pin
+    ``DPLL_A_PIN_FREQUENCY``           attr requested frequency of a pin
+    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent
+                                       device configuration of a pin
+      ``DPLL_A_ID``                    attr parent dpll device id
+      ``DPLL_A_PIN_PRIO``              attr requested priority of pin on
+                                       the dpll device
+      ``DPLL_A_PIN_STATE``             attr requested state of pin on
+                                       the dpll device
+    ``DPLL_A_PIN_PARENT_PIN``          nested attr for each parent pin
+                                       configuration
+      ``DPLL_A_PIN_ID``                attr parent pin id
+      ``DPLL_A_PIN_STATE``             attr requested state of pin on
+                                       parent pin
+  ==================================== =================================
+
+Netlink dump requests
+=====================
+
+The ``DPLL_CMD_DEVICE_GET`` and ``DPLL_CMD_PIN_GET`` commands are
+capable of dump type netlink requests, in which case the response is in
+the same format as for their ``do`` request, but every device or pin
+registered in the system is returned.
+
+SET commands format
+===================
+
+``DPLL_CMD_DEVICE_SET`` - to target a dpll device, the user provides
+``DPLL_A_ID``, which is unique identifier of dpll device in the system,
+as well as parameter being configured (``DPLL_A_MODE``).
+
+``DPLL_CMD_PIN_SET`` - to target a pin user has to provide a
+``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
+Also configured pin parameters must be added.
+If ``DPLL_A_PIN_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
+this affects all the dpll device they are connected, that is why those
+attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
+Other attributes:
+``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
+``DPLL_A_PIN_PARENT`` as their configuration relates to only one
+parent dpll or parent pin.
+Nested attribute of either ``DPLL_A_ID`` or ``DPLL_A_PIN_ID`` determines
+if configuration was requested on a dpll device or on a pin
+respectively.
+In general, it is possible to configure multiple parameters at once, but
+internally each parameter change will be invoked separately, where order
+of configuration is not guaranteed by any means.
+
+Configuration pre-defined enums
+===============================
+
+.. kernel-doc:: include/uapi/linux/dpll.h
+
+Notifications
+=============
+
+dpll device can provide notifications regarding status changes of the
+device, i.e. lock status changes, input/output changes or other alarms.
+There is one multicast group that is used to notify user-space apps via
+netlink socket: ``DPLL_MCGRP_MONITOR``
+
+Notifications messages:
+
+  ============================== =====================================
+  ``DPLL_CMD_DEVICE_CREATE_NTF`` dpll device was created
+  ``DPLL_CMD_DEVICE_DELETE_NTF`` dpll device was deleted
+  ``DPLL_CMD_DEVICE_CHANGE_NTF`` dpll device has changed
+  ``DPLL_CMD_PIN_CREATE_NTF``    dpll pin was created
+  ``DPLL_CMD_PIN_DELETE_NTF``    dpll pin was deleted
+  ``DPLL_CMD_PIN_CHANGE_NTF``    dpll pin has changed
+  ============================== =====================================
+
+Events format is the same as for the corresponding get command.
+Format of ``DPLL_CMD_DEVICE_`` events is the same as response of
+``DPLL_CMD_DEVICE_GET``.
+Format of ``DPLL_CMD_PIN_`` events is same as response of
+``DPLL_CMD_PIN_GET``.
+
+Device driver implementation
+============================
+
+Device is allocated by dpll_device_get() call. Second call with the
+same arguments will not create new object but provides pointer to
+previously created device for given arguments, it also increases
+refcount of that object.
+Device is deallocated by dpll_device_put() call, which first
+decreases the refcount, once refcount is cleared the object is
+destroyed.
+
+Device should implement set of operations and register device via
+dpll_device_register() at which point it becomes available to the
+users. Multiple driver instances can obtain reference to it with
+dpll_device_get(), as well as register dpll device with their own
+ops and priv.
+
+The pins are allocated separately with dpll_pin_get(), it works
+similarly to dpll_device_get(). Function first creates object and then
+for each call with the same arguments only the object refcount
+increases. Also dpll_pin_put() works similarly to dpll_device_put().
+
+A pin can be registered with parent dpll device or parent pin, depending
+on hardware needs. Each registration requires registerer to provide set
+of pin callbacks, and private data pointer for calling them:
+
+- dpll_pin_register() - register pin with a dpll device,
+- dpll_pin_on_pin_register() - register pin with another MUX type pin.
+
+Notifications of adding or removing dpll devices are created within
+subsystem itself.
+Notifications about registering/deregistering pins are also invoked by
+the subsystem.
+Notifications about status changes either of dpll device or a pin are
+invoked in two ways:
+
+- after successful change was requested on dpll subsystem, the subsystem
+  calls corresponding notification,
+- requested by device driver with dpll_device_change_ntf() or
+  dpll_pin_change_ntf() when driver informs about the status change.
+
+The device driver using dpll interface is not required to implement all
+the callback operation. Neverthelessi, there are few required to be
+implemented.
+Required dpll device level callback operations:
+
+- ``.mode_get``,
+- ``.lock_status_get``.
+
+Required pin level callback operations:
+
+- ``.state_on_dpll_get`` (pins registered with dpll device),
+- ``.state_on_pin_get`` (pins registered with parent pin),
+- ``.direction_get``.
+
+Every other operation handler is checked for existence and
+``-EOPNOTSUPP`` is returned in case of absence of specific handler.
+
+SyncE enablement
+================
+For SyncE enablement it is required to allow control over dpll device
+for a software application which monitors and configures the inputs of
+dpll device in response to current state of a dpll device and its
+inputs.
+In such scenario, dpll device input signal shall be also configurable
+to drive dpll with signal recovered from the PHY netdevice.
+This is done by exposing a pin to the netdevice - attaching pin to the
+netdevice itself with
+``netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)``.
+Exposed pin id handle ``DPLL_A_PIN_ID`` is then identifiable by the user
+as it is attached to rtnetlink respond to get ``RTM_NEWLINK`` command in
+nested attribute ``IFLA_DPLL_PIN``.
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index 1e16a40da3ba..f549a68951d7 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -114,6 +114,7 @@ available subsections can be seen below.
    zorro
    hte/index
    wmi
+   dpll
 
 .. only::  subproject and html
 
-- 
2.27.0


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

* [PATCH net-next 03/11] dpll: documentation on DPLL subsystem interface
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche,
	Bagas Sanjaya

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.

Co-developed-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- fix muxed get-pin command respond example
- fix typos
- describe DPLL_MODE_FREERUN

v8->v9:
- fix docs build warnings
- separate netlink command/attribute list
- replace enum description with uapi header
- add brief explanation what is a DPLL
- fix EOPNOTSUPP typo
- fix typo .state_get -> .state_on_dpll_get

 Documentation/driver-api/dpll.rst  | 431 +++++++++++++++++++++++++++++
 Documentation/driver-api/index.rst |   1 +
 2 files changed, 432 insertions(+)
 create mode 100644 Documentation/driver-api/dpll.rst

diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
new file mode 100644
index 000000000000..c11dcb74d11e
--- /dev/null
+++ b/Documentation/driver-api/dpll.rst
@@ -0,0 +1,431 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux kernel dpll subsystem
+===============================
+
+DPLL
+====
+
+PLL - Phase Locked Loop is an electronic circuit which syntonizes clock
+signal of a device with an external clock signal. Effectively enabling
+device to run on the same clock signal beat as provided on a PLL input.
+
+DPLL - Digital Phase Locked Loop is am integrated circuit which in
+addition to plain PLL behavior incorporates a digital phase detector
+and may have digital divider in the loop. As a result, the frequency on
+DPLL's input and output may be configurable.
+
+Subsystem
+=========
+
+The main purpose of dpll subsystem is to provide general interface
+to configure devices that use any kind of Digital PLL and could use
+different sources of input signal to synchronize to, as well as
+different types of outputs.
+The main interface is NETLINK_GENERIC based protocol with an event
+monitoring multicast group defined.
+
+Device object
+=============
+
+Single dpll device object means single Digital PLL circuit and bunch of
+connected pins.
+It reports the supported modes of operation and current status to the
+user in response to the `do` request of netlink command
+``DPLL_CMD_DEVICE_GET`` and list of dplls registered in the subsystem
+with `dump` netlink request of the same command.
+Changing the configuration of dpll device is done with `do` request of
+netlink ``DPLL_CMD_DEVICE_SET`` command.
+A device handle is ``DPLL_A_ID``, it shall be provided to get or set
+configuration of particular device in the system. It can be obtained
+with a ``DPLL_CMD_DEVICE_GET`` `dump` request or
+a ``DPLL_CMD_DEVICE_ID_GET`` `do` request, where the one must provide
+attributes that result in single device match.
+
+Pin object
+==========
+
+A pin is amorphic object which represents either input or output, it
+could be internal component of the device, as well as externally
+connected.
+The number of pins per dpll vary, but usually multiple pins shall be
+provided for a single dpll device.
+Pin's properties, capabilities and status is provided to the user in
+response to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
+It is also possible to list all the pins that were registered in the
+system with `dump` request of ``DPLL_CMD_PIN_GET`` command.
+Configuration of a pin can be changed by `do` request of netlink
+``DPLL_CMD_PIN_SET`` command.
+Pin handle is a ``DPLL_A_PIN_ID``, it shall be provided to get or set
+configuration of particular pin in the system. It can be obtained with
+``DPLL_CMD_PIN_GET`` `dump` request or ``DPLL_CMD_PIN_ID_GET`` `do`
+request, where user provides attributes that result in single pin match.
+
+Pin selection
+=============
+
+In general, selected pin (the one which signal is driving the dpll
+device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
+one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
+device.
+
+Pin selection can be done either manually or automatically, depending
+on hardware capabilities and active dpll device work mode
+(``DPLL_A_MODE`` attribute). The consequence is that there are
+differences for each mode in terms of available pin states, as well as
+for the states the user can request for a dpll device.
+
+In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
+one of following pin states:
+
+- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
+  device
+
+In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
+receive one of following pin states:
+
+- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
+  input for automatic selection algorithm
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
+  a valid input for automatic selection algorithm
+
+In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
+pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
+algorithm locks a dpll device with one of the inputs.
+
+For other dpll device operating modes there is no pin selection
+mechanics.
+
+Shared pins
+===========
+
+A single pin object can be attached to multiple dpll devices.
+Then there are two groups of configuration knobs:
+
+1) Set on a pin - the configuration affects all dpll devices pin is
+   registered to (i.e. ``DPLL_A_PIN_FREQUENCY``),
+2) Set on a pin-dpll tuple - the configuration affects only selected
+   dpll device (i.e. ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
+   ``DPLL_A_PIN_DIRECTION``).
+
+MUX-type pins
+=============
+
+A pin can be MUX-type, it aggregates child pins and serves as a pin
+multiplexer. One or more pins are registered with MUX-type instead of
+being directly registered to a dpll device.
+Pins registered with a MUX-type pin provide user with additional nested
+attribute ``DPLL_A_PIN_PARENT`` for each parent they were registered
+with.
+If a pin was registered with multiple parent pins, they behave like a
+multiple output multiplexer. In this case output of a
+``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
+attributes with current state related to each parent, like::
+
+  'pin': [{
+   {'clock-id': 282574471561216,
+    'module-name': 'ice',
+    'pin-dpll-caps': 4,
+    'pin-id': 13,
+    'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
+                   {'pin-id': 3, 'pin-state': 'disconnected'}],
+    'pin-type': 'synce-eth-port'}
+  }]
+
+Only one child pin can provide its signal to the parent MUX-type pin at
+a time, the selection is done by requesting change of a child pin state
+on desired parent, with the use of ``DPLL_A_PIN_PARENT`` nested
+attribute. Example of netlink `set state on parent pin` message format:
+
+  ====================== =============================================
+  ``DPLL_A_PIN_ID``      child pin id
+  ``DPLL_A_PIN_PARENT``  nested attribute for requesting configuration
+                         related to parent pin
+    ``DPLL_A_PIN_ID``    parent pin id
+    ``DPLL_A_PIN_STATE`` requested pin state on parent
+  ====================== =============================================
+
+Pin priority
+============
+
+Some devices might offer a capability of automatic pin selection mode
+(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
+Usually, automatic selection is performed on the hardware level, which
+means only pins directly connected to the dpll can be used for automatic
+input pin selection.
+In automatic selection mode, the user cannot manually select a input
+pin for the device, instead the user shall provide all directly
+connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
+pick a highest priority valid signal and use it to control the DPLL
+device. Example of netlink `set priority on parent pin` message format:
+
+  =====================  =============================================
+  ``DPLL_A_PIN_ID``      child pin id
+  ``DPLL_A_PIN_PARENT``  nested attribute for requesting configuration
+                         related to parent pin
+    ``DPLL_A_ID``        parent dpll id
+    ``DPLL_A_PIN_PRIO``  requested pin prio on parent dpll
+  =====================  =============================================
+
+Child pin of MUX-type is not capable of automatic input pin selection,
+in order to configure a input of a MUX-type pin, the user needs to
+request desired pin state of the child pin on the parent pin,
+as described in the ``MUX-type pins`` chapter.
+
+Configuration commands group
+============================
+
+Configuration commands are used to get information about registered
+dpll devices (and pins), as well as set configuration of device or pins.
+As dpll devices must be abstracted and reflect real hardware,
+there is no way to add new dpll device via netlink from user space and
+each device should be registered by its driver.
+
+All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
+any spamming/DoS from unauthorized userspace applications.
+
+List of netlink commands with possible attributes
+=================================================
+
+All constants identifying command types use a ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use a ``DPLL_A_``
+prefix and suffix according to attribute purpose:
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_ID_GET``           command to get device ID
+  ``DPLL_A_MODULE_NAME``               attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_TYPE``                    attr type of dpll device
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_GET``              command to get device info or
+                                       dump list of available devices
+    ``DPLL_A_ID``                      attr unique dpll device ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_MODE``                    attr selection mode
+    ``DPLL_A_MODE_SUPPORTED``          attr available selection modes
+    ``DPLL_A_LOCK_STATUS``             attr dpll device lock status
+    ``DPLL_A_TEMP``                    attr device temperature info
+    ``DPLL_A_TYPE``                    attr type of dpll device
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_DEVICE_SET``              command to set dpll device config
+    ``DPLL_A_ID``                      attr internal dpll device index
+    ``DPLL_A_MODE``                    attr selection mode to configure
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_ID_GET``              command to get pin ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_PIN_BOARD_LABEL``         attr pin board label provided
+                                       by registerer
+    ``DPLL_A_PIN_PANEL_LABEL``         attr pin panel label provided
+                                       by registerer
+    ``DPLL_A_PIN_PACKAGE_LABEL``       attr pin package label provided
+                                       by registerer
+    ``DPLL_A_PIN_TYPE``                attr type of a pin
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_GET``                 command to get pin info or dump
+                                       list of available pins
+    ``DPLL_A_PIN_ID``                  attr unique a pin ID
+    ``DPLL_A_MODULE_NAME``             attr module name of registerer
+    ``DPLL_A_CLOCK_ID``                attr Unique Clock Identifier
+                                       (EUI-64), as defined by the
+                                       IEEE 1588 standard
+    ``DPLL_A_PIN_BOARD_LABEL``         attr pin board label provided
+                                       by registerer
+    ``DPLL_A_PIN_PANEL_LABEL``         attr pin panel label provided
+                                       by registerer
+    ``DPLL_A_PIN_PACKAGE_LABEL``       attr pin package label provided
+                                       by registerer
+    ``DPLL_A_PIN_TYPE``                attr type of a pin
+    ``DPLL_A_PIN_DIRECTION``           attr direction of a pin
+    ``DPLL_A_PIN_FREQUENCY``           attr current frequency of a pin
+    ``DPLL_A_PIN_FREQUENCY_SUPPORTED`` nested attr provides supported
+                                       frequencies
+      ``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
+      ``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
+    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent device
+                                       the pin is connected with
+      ``DPLL_A_ID``                    attr provided if parent is dpll
+                                       device
+      ``DPLL_A_PIN_PRIO``              attr priority of pin on the
+                                       dpll device
+      ``DPLL_A_PIN_STATE``             attr state of pin on the parent
+                                       dpll device
+    ``DPLL_A_PIN_PARENT_PIN``          nested attr for each parent pin
+                                       the pin is connected with
+      ``DPLL_A_PIN_ID``                attr provided if parent is a pin
+      ``DPLL_A_PIN_STATE``             attr state of pin on the parent
+                                       pin
+    ``DPLL_A_PIN_DPLL_CAPS``           attr bitmask of pin-dpll
+                                       capabilities
+  ==================================== =================================
+
+  ==================================== =================================
+  ``DPLL_CMD_PIN_SET``                 command to set pins configuration
+    ``DPLL_A_PIN_ID``                  attr unique a pin ID
+    ``DPLL_A_PIN_DIRECTION``           attr requested direction of a pin
+    ``DPLL_A_PIN_FREQUENCY``           attr requested frequency of a pin
+    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent
+                                       device configuration of a pin
+      ``DPLL_A_ID``                    attr parent dpll device id
+      ``DPLL_A_PIN_PRIO``              attr requested priority of pin on
+                                       the dpll device
+      ``DPLL_A_PIN_STATE``             attr requested state of pin on
+                                       the dpll device
+    ``DPLL_A_PIN_PARENT_PIN``          nested attr for each parent pin
+                                       configuration
+      ``DPLL_A_PIN_ID``                attr parent pin id
+      ``DPLL_A_PIN_STATE``             attr requested state of pin on
+                                       parent pin
+  ==================================== =================================
+
+Netlink dump requests
+=====================
+
+The ``DPLL_CMD_DEVICE_GET`` and ``DPLL_CMD_PIN_GET`` commands are
+capable of dump type netlink requests, in which case the response is in
+the same format as for their ``do`` request, but every device or pin
+registered in the system is returned.
+
+SET commands format
+===================
+
+``DPLL_CMD_DEVICE_SET`` - to target a dpll device, the user provides
+``DPLL_A_ID``, which is unique identifier of dpll device in the system,
+as well as parameter being configured (``DPLL_A_MODE``).
+
+``DPLL_CMD_PIN_SET`` - to target a pin user has to provide a
+``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
+Also configured pin parameters must be added.
+If ``DPLL_A_PIN_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
+this affects all the dpll device they are connected, that is why those
+attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
+Other attributes:
+``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
+``DPLL_A_PIN_PARENT`` as their configuration relates to only one
+parent dpll or parent pin.
+Nested attribute of either ``DPLL_A_ID`` or ``DPLL_A_PIN_ID`` determines
+if configuration was requested on a dpll device or on a pin
+respectively.
+In general, it is possible to configure multiple parameters at once, but
+internally each parameter change will be invoked separately, where order
+of configuration is not guaranteed by any means.
+
+Configuration pre-defined enums
+===============================
+
+.. kernel-doc:: include/uapi/linux/dpll.h
+
+Notifications
+=============
+
+dpll device can provide notifications regarding status changes of the
+device, i.e. lock status changes, input/output changes or other alarms.
+There is one multicast group that is used to notify user-space apps via
+netlink socket: ``DPLL_MCGRP_MONITOR``
+
+Notifications messages:
+
+  ============================== =====================================
+  ``DPLL_CMD_DEVICE_CREATE_NTF`` dpll device was created
+  ``DPLL_CMD_DEVICE_DELETE_NTF`` dpll device was deleted
+  ``DPLL_CMD_DEVICE_CHANGE_NTF`` dpll device has changed
+  ``DPLL_CMD_PIN_CREATE_NTF``    dpll pin was created
+  ``DPLL_CMD_PIN_DELETE_NTF``    dpll pin was deleted
+  ``DPLL_CMD_PIN_CHANGE_NTF``    dpll pin has changed
+  ============================== =====================================
+
+Events format is the same as for the corresponding get command.
+Format of ``DPLL_CMD_DEVICE_`` events is the same as response of
+``DPLL_CMD_DEVICE_GET``.
+Format of ``DPLL_CMD_PIN_`` events is same as response of
+``DPLL_CMD_PIN_GET``.
+
+Device driver implementation
+============================
+
+Device is allocated by dpll_device_get() call. Second call with the
+same arguments will not create new object but provides pointer to
+previously created device for given arguments, it also increases
+refcount of that object.
+Device is deallocated by dpll_device_put() call, which first
+decreases the refcount, once refcount is cleared the object is
+destroyed.
+
+Device should implement set of operations and register device via
+dpll_device_register() at which point it becomes available to the
+users. Multiple driver instances can obtain reference to it with
+dpll_device_get(), as well as register dpll device with their own
+ops and priv.
+
+The pins are allocated separately with dpll_pin_get(), it works
+similarly to dpll_device_get(). Function first creates object and then
+for each call with the same arguments only the object refcount
+increases. Also dpll_pin_put() works similarly to dpll_device_put().
+
+A pin can be registered with parent dpll device or parent pin, depending
+on hardware needs. Each registration requires registerer to provide set
+of pin callbacks, and private data pointer for calling them:
+
+- dpll_pin_register() - register pin with a dpll device,
+- dpll_pin_on_pin_register() - register pin with another MUX type pin.
+
+Notifications of adding or removing dpll devices are created within
+subsystem itself.
+Notifications about registering/deregistering pins are also invoked by
+the subsystem.
+Notifications about status changes either of dpll device or a pin are
+invoked in two ways:
+
+- after successful change was requested on dpll subsystem, the subsystem
+  calls corresponding notification,
+- requested by device driver with dpll_device_change_ntf() or
+  dpll_pin_change_ntf() when driver informs about the status change.
+
+The device driver using dpll interface is not required to implement all
+the callback operation. Neverthelessi, there are few required to be
+implemented.
+Required dpll device level callback operations:
+
+- ``.mode_get``,
+- ``.lock_status_get``.
+
+Required pin level callback operations:
+
+- ``.state_on_dpll_get`` (pins registered with dpll device),
+- ``.state_on_pin_get`` (pins registered with parent pin),
+- ``.direction_get``.
+
+Every other operation handler is checked for existence and
+``-EOPNOTSUPP`` is returned in case of absence of specific handler.
+
+SyncE enablement
+================
+For SyncE enablement it is required to allow control over dpll device
+for a software application which monitors and configures the inputs of
+dpll device in response to current state of a dpll device and its
+inputs.
+In such scenario, dpll device input signal shall be also configurable
+to drive dpll with signal recovered from the PHY netdevice.
+This is done by exposing a pin to the netdevice - attaching pin to the
+netdevice itself with
+``netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)``.
+Exposed pin id handle ``DPLL_A_PIN_ID`` is then identifiable by the user
+as it is attached to rtnetlink respond to get ``RTM_NEWLINK`` command in
+nested attribute ``IFLA_DPLL_PIN``.
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index 1e16a40da3ba..f549a68951d7 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -114,6 +114,7 @@ available subsections can be seen below.
    zorro
    hte/index
    wmi
+   dpll
 
 .. only::  subproject and html
 
-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 04/11] dpll: spec: Add Netlink spec in YAML
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---

RFC v9->v0:
- add DPLL_MODE_FREERUN

v8->v9:
- regenerate policy max values
- add missing enum descriptions
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple
- fix typos:
  - s/working-modes/working modes/
  - s/differentiate/differentiates/
  - s/valid input, auto selected by dpll/input pin auto selected by dpll/
- remove FREERUN and HOLDOVER modes

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


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

* [PATCH net-next 04/11] dpll: spec: Add Netlink spec in YAML
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---

RFC v9->v0:
- add DPLL_MODE_FREERUN

v8->v9:
- regenerate policy max values
- add missing enum descriptions
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple
- fix typos:
  - s/working-modes/working modes/
  - s/differentiate/differentiates/
  - s/valid input, auto selected by dpll/input pin auto selected by dpll/
- remove FREERUN and HOLDOVER modes

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

* [PATCH net-next 05/11] dpll: core: Add DPLL framework base functions
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

Co-developed-by: Milena Olech <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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- remove unused mode_supported_mask from dpll_device
- fix increment refcount only if it was not already set
- remove static function comments/fix return descriptions
- add missing mutex locking on getting/registering pins
- check if dpll is registered before registering pin
- headers are squashed into this commit
- updated MAINTAINERS to add Arkadiusz and Jiri as maintainers

v8->v9:
- fix description in spdx header.
- remove refcount check if refcount was already set
- do not validate dpll ptr in dpll_device_put(..)
- fix return -ENOMEM on failed memory alloc
- do not validate pin ptr in dpll_pin_put(..)
- return -EINVAL in case of module/clock_id mismatch
- do not {} around one-line xa_for_each() macro
- move dpll_<x>_registration structs to dpll_core.c
- rephrase doc comment on device and pin id struct members
- remove ref in case of memory allocation fail
- check for required ops on pin/device registration
- mark pin with DPLL_REGISTERED once pin is registered with dpll

 MAINTAINERS              |  11 +
 drivers/Kconfig          |   2 +
 drivers/Makefile         |   1 +
 drivers/dpll/Kconfig     |   7 +
 drivers/dpll/Makefile    |   9 +
 drivers/dpll/dpll_core.c | 799 +++++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h |  90 +++++
 include/linux/dpll.h     | 136 +++++++
 8 files changed, 1055 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 include/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9a5863f1b016..54050447ec43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6304,6 +6304,17 @@ 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 SUBSYSTEM
+M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
+M:	Jiri Pirko <jiri@resnulli.us>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	Documentation/driver-api/dpll.rst
+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 514ae6b24cb2..ce5f63918eba 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
 
 source "drivers/cdx/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7241d80a7b29..6fea42a6dd05 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -195,3 +195,4 @@ obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
 obj-$(CONFIG_DRM_ACCEL)		+= accel/
 obj-$(CONFIG_CDX_BUS)		+= cdx/
+obj-$(CONFIG_DPLL)		+= dpll/
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
new file mode 100644
index 000000000000..a4cae73f20d3
--- /dev/null
+++ b/drivers/dpll/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic DPLL drivers configuration
+#
+
+config DPLL
+  bool
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
new file mode 100644
index 000000000000..2e5b27850110
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)      += dpll.o
+dpll-y                  += dpll_core.o
+dpll-y                  += dpll_netlink.o
+dpll-y                  += dpll_nl.o
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..18270c58e3ac
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - DPLL subsystem kernel-space interface implementation.
+ *
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel Corporation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dpll_core.h"
+
+DEFINE_MUTEX(dpll_lock);
+
+DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
+DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
+
+#define ASSERT_DPLL_REGISTERED(d)	\
+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_DPLL_NOT_REGISTERED(d)	\
+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_PIN_REGISTERED(p)	\
+	WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED))
+
+struct dpll_device_registration {
+	struct list_head list;
+	const struct dpll_device_ops *ops;
+	void *priv;
+};
+
+struct dpll_pin_registration {
+	struct list_head list;
+	const struct dpll_pin_ops *ops;
+	void *priv;
+};
+
+struct dpll_device *dpll_device_get_by_id(int id)
+{
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		return xa_load(&dpll_device_xa, id);
+
+	return NULL;
+}
+
+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;
+}
+
+static int
+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
+		    const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	bool ref_exists = false;
+	unsigned long i;
+	int ret;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+		ref_exists = true;
+		break;
+	}
+
+	if (!ref_exists) {
+		ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+		if (!ref)
+			return -ENOMEM;
+		ref->pin = pin;
+		INIT_LIST_HEAD(&ref->registration_list);
+		ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
+		}
+		refcount_set(&ref->refcount, 1);
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists) {
+			xa_erase(xa_pins, pin->pin_idx);
+			kfree(ref);
+		}
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	if (ref_exists)
+		refcount_inc(&ref->refcount);
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
+}
+
+static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
+			       const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return -EINVAL;
+		if (refcount_dec_and_test(&ref->refcount)) {
+			list_del(&reg->list);
+			kfree(reg);
+			xa_erase(xa_pins, i);
+			WARN_ON(!list_empty(&ref->registration_list));
+			kfree(ref);
+		}
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
+		     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	bool ref_exists = false;
+	unsigned long i;
+	int ret;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+		ref_exists = true;
+		break;
+	}
+
+	if (!ref_exists) {
+		ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+		if (!ref)
+			return -ENOMEM;
+		ref->dpll = dpll;
+		INIT_LIST_HEAD(&ref->registration_list);
+		ret = xa_insert(xa_dplls, dpll->device_idx, ref, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
+		}
+		refcount_set(&ref->refcount, 1);
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists) {
+			xa_erase(xa_dplls, dpll->device_idx);
+			kfree(ref);
+		}
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	if (ref_exists)
+		refcount_inc(&ref->refcount);
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
+}
+
+static void
+dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
+		     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return;
+		if (refcount_dec_and_test(&ref->refcount)) {
+			list_del(&reg->list);
+			kfree(reg);
+			xa_erase(xa_dplls, i);
+			WARN_ON(!list_empty(&ref->registration_list));
+			kfree(ref);
+		}
+		return;
+	}
+}
+
+struct dpll_pin_ref *
+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_refs, i, ref)
+		if (ref->dpll == dpll)
+			return ref;
+
+	return NULL;
+}
+
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i = 0;
+
+	ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
+	WARN_ON(!ref);
+	return ref;
+}
+
+static struct dpll_device *
+dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
+{
+	struct dpll_device *dpll;
+	int ret;
+
+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+	if (!dpll)
+		return ERR_PTR(-ENOMEM);
+	refcount_set(&dpll->refcount, 1);
+	INIT_LIST_HEAD(&dpll->registration_list);
+	dpll->device_idx = device_idx;
+	dpll->clock_id = clock_id;
+	dpll->module = module;
+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b,
+		       GFP_KERNEL);
+	if (ret) {
+		kfree(dpll);
+		return ERR_PTR(ret);
+	}
+	xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
+
+	return dpll;
+}
+
+/**
+ * dpll_device_get - find existing or create new dpll device
+ * @clock_id: clock_id of creator
+ * @device_idx: idx given by device driver
+ * @module: reference to registering module
+ *
+ * Get existing object of a dpll device, unique for given arguments.
+ * Create new if doesn't exist yet.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR(X) - error
+ */
+struct dpll_device *
+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_lock);
+	xa_for_each(&dpll_device_xa, index, dpll) {
+		if (dpll->clock_id == clock_id &&
+		    dpll->device_idx == device_idx &&
+		    dpll->module == module) {
+			ret = dpll;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_device_alloc(clock_id, device_idx, module);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_get);
+
+/**
+ * dpll_device_put - decrease the refcount and free memory if possible
+ * @dpll: dpll_device struct pointer
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Drop reference for a dpll device, if all references are gone, delete
+ * dpll device object.
+ */
+void dpll_device_put(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll_lock);
+	if (refcount_dec_and_test(&dpll->refcount)) {
+		ASSERT_DPLL_NOT_REGISTERED(dpll);
+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+		xa_destroy(&dpll->pin_refs);
+		xa_erase(&dpll_device_xa, dpll->id);
+		WARN_ON(!list_empty(&dpll->registration_list));
+		kfree(dpll);
+	}
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_put);
+
+static struct dpll_device_registration *
+dpll_device_registration_find(struct dpll_device *dpll,
+			      const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	list_for_each_entry(reg, &dpll->registration_list, list) {
+		if (reg->ops == ops && reg->priv == priv)
+			return reg;
+	}
+	return NULL;
+}
+
+/**
+ * dpll_device_register - register the dpll device in the subsystem
+ * @dpll: pointer to a dpll
+ * @type: type of a dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Make dpll device available for user space.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+	bool first_registration = false;
+
+	if (WARN_ON(!ops))
+		return -EINVAL;
+	if (WARN_ON(!ops->mode_get))
+		return -EINVAL;
+	if (WARN_ON(!ops->lock_status_get))
+		return -EINVAL;
+	if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
+		mutex_unlock(&dpll_lock);
+		return -EEXIST;
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_lock);
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	dpll->type = type;
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_lock);
+		return 0;
+	}
+
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_lock);
+	dpll_device_create_ntf(dpll);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+/**
+ * dpll_device_unregister - unregister dpll device
+ * @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Unregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	mutex_lock(&dpll_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+	dpll_device_delete_ntf(dpll);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_lock);
+		return;
+	}
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_unregister);
+
+static struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	pin->pin_idx = pin_idx;
+	pin->clock_id = clock_id;
+	pin->module = module;
+	if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop = prop;
+	refcount_set(&pin->refcount, 1);
+	xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
+	xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
+	ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
+	if (ret)
+		goto err;
+	return pin;
+err:
+	xa_destroy(&pin->dpll_refs);
+	xa_destroy(&pin->parent_refs);
+	kfree(pin);
+	return ERR_PTR(ret);
+}
+
+/**
+ * dpll_pin_get - find existing or create new dpll pin
+ * @clock_id: clock_id of creator
+ * @pin_idx: idx given by dev driver
+ * @module: reference to registering module
+ * @prop: dpll pin properties
+ *
+ * Get existing object of a pin (unique for given arguments) or create new
+ * if doesn't exist yet.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid allocated dpll_pin struct pointer if succeeded
+ * * ERR_PTR(X) - error
+ */
+struct dpll_pin *
+dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
+	     const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pos, *ret = NULL;
+	unsigned long i;
+
+	mutex_lock(&dpll_lock);
+	xa_for_each(&dpll_pin_xa, i, pos) {
+		if (pos->clock_id == clock_id &&
+		    pos->pin_idx == pin_idx &&
+		    pos->module == module) {
+			ret = pos;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_get);
+
+/**
+ * dpll_pin_put - decrease the refcount and free memory if possible
+ * @pin: pointer to a pin to be put
+ *
+ * Drop reference for a pin, if all references are gone, delete pin object.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_pin_put(struct dpll_pin *pin)
+{
+	mutex_lock(&dpll_lock);
+	if (refcount_dec_and_test(&pin->refcount)) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		xa_erase(&dpll_pin_xa, pin->id);
+		kfree(pin);
+	}
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_put);
+
+static int
+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		    const struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
+	if (ret)
+		return ret;
+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
+	if (ret)
+		goto ref_pin_del;
+	xa_set_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
+	dpll_pin_create_ntf(pin);
+
+	return ret;
+
+ref_pin_del:
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+	return ret;
+}
+
+/**
+ * dpll_pin_register - register the dpll pin in the subsystem
+ * @dpll: pointer to a dpll
+ * @pin: pointer to a dpll pin
+ * @ops: ops for a dpll pin ops
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int
+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		  const struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	if (WARN_ON(!ops) ||
+	    WARN_ON(!ops->state_on_dpll_get) ||
+	    WARN_ON(!ops->direction_get))
+		return -EINVAL;
+	if (ASSERT_DPLL_REGISTERED(dpll))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	if (WARN_ON(!(dpll->module == pin->module &&
+		      dpll->clock_id == pin->clock_id)))
+		ret = -EINVAL;
+	else
+		ret = __dpll_pin_register(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+static void
+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv)
+{
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
+	if (xa_empty(&pin->dpll_refs))
+		xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
+}
+
+/**
+ * dpll_pin_unregister - unregister dpll pin from dpll device
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv)
+{
+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
+		return;
+	if (WARN_ON(!xa_empty(&pin->parent_refs)))
+		return;
+
+	mutex_lock(&dpll_lock);
+	dpll_pin_delete_ntf(pin);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_unregister);
+
+/**
+ * dpll_pin_on_pin_register - register a pin with a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Register a pin with a parent pin, create references between them and
+ * between newly registered pin and dplls connected with a parent pin.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i, stop;
+	int ret;
+
+	if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
+		return -EINVAL;
+
+	if (WARN_ON(!ops) ||
+	    WARN_ON(!ops->state_on_pin_get) ||
+	    WARN_ON(!ops->direction_get))
+		return -EINVAL;
+	if (ASSERT_PIN_REGISTERED(parent))
+		return -EINVAL;
+
+	mutex_lock(&dpll_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) {
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_create_ntf(pin);
+	}
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref)
+		if (i < stop) {
+			dpll_pin_delete_ntf(pin);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+		}
+	refcount_dec(&pin->refcount);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+unlock:
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
+
+/**
+ * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Note: It does not free the memory
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	mutex_lock(&dpll_lock);
+	dpll_pin_delete_ntf(pin);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+	refcount_dec(&pin->refcount);
+	xa_for_each(&pin->dpll_refs, i, ref)
+		__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+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;
+}
+
+void *dpll_priv(struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first(dpll);
+	return reg->priv;
+}
+
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first(dpll);
+	return reg->ops;
+}
+
+static struct dpll_pin_registration *
+dpll_pin_registration_first(struct dpll_pin_ref *ref)
+{
+	struct dpll_pin_registration *reg;
+
+	reg = list_first_entry_or_null(&ref->registration_list,
+				       struct dpll_pin_registration, list);
+	WARN_ON(!reg);
+	return reg;
+}
+
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
+			    struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load(&dpll->pin_refs, pin->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
+			   struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load(&pin->parent_refs, parent->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
+{
+	struct dpll_pin_registration *reg;
+
+	reg = dpll_pin_registration_first(ref);
+	return reg->ops;
+}
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	return 0;
+
+error:
+	mutex_destroy(&dpll_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..3574bebf2c63
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_CORE_H__
+#define __DPLL_CORE_H__
+
+#include <linux/dpll.h>
+#include <linux/list.h>
+#include <linux/refcount.h>
+#include "dpll_netlink.h"
+
+#define DPLL_REGISTERED		XA_MARK_1
+
+/**
+ * struct dpll_device - stores DPLL device internal data
+ * @id:			unique id number for device given by dpll subsystem
+ * @device_idx:		id given by dev driver
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @module:		module of creator
+ * @type:		type of a dpll
+ * @pin_refs:		stores pins registered within a dpll
+ * @refcount:		refcount
+ * @registration_list:	list of registered ops and priv data of dpll owners
+ **/
+struct dpll_device {
+	u32 id;
+	u32 device_idx;
+	u64 clock_id;
+	struct module *module;
+	enum dpll_type type;
+	struct xarray pin_refs;
+	refcount_t refcount;
+	struct list_head registration_list;
+};
+
+/**
+ * struct dpll_pin - structure for a dpll pin
+ * @id:			unique id number for pin given by dpll subsystem
+ * @pin_idx:		index of a pin given by dev driver
+ * @clock_id:		clock_id of creator
+ * @module:		module of creator
+ * @dpll_refs:		hold referencees to dplls pin was registered with
+ * @parent_refs:	hold references to parent pins pin was registered with
+ * @prop:		pointer to pin properties given by registerer
+ * @rclk_dev_name:	holds name of device when pin can recover clock from it
+ * @refcount:		refcount
+ **/
+struct dpll_pin {
+	u32 id;
+	u32 pin_idx;
+	u64 clock_id;
+	struct module *module;
+	struct xarray dpll_refs;
+	struct xarray parent_refs;
+	const struct dpll_pin_properties *prop;
+	char *rclk_dev_name;
+	refcount_t refcount;
+};
+
+/**
+ * struct dpll_pin_ref - structure for referencing either dpll or pins
+ * @dpll:		pointer to a dpll
+ * @pin:		pointer to a pin
+ * @registration_list:	list of ops and priv data registered with the ref
+ * @refcount:		refcount
+ **/
+struct dpll_pin_ref {
+	union {
+		struct dpll_device *dpll;
+		struct dpll_pin *pin;
+	};
+	struct list_head registration_list;
+	refcount_t refcount;
+};
+
+void *dpll_priv(struct dpll_device *dpll);
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
+
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
+struct dpll_device *dpll_device_get_by_id(int id);
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
+extern struct xarray dpll_device_xa;
+extern struct xarray dpll_pin_xa;
+extern struct mutex dpll_lock;
+#endif
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..d3258205e499
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_H__
+#define __DPLL_H__
+
+#include <uapi/linux/dpll.h>
+#include <linux/device.h>
+#include <linux/netlink.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
+			enum dpll_mode *mode, struct netlink_ext_ack *extack);
+	int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
+			const enum dpll_mode mode,
+			struct netlink_ext_ack *extack);
+	bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
+			       const enum dpll_mode mode,
+			       struct netlink_ext_ack *extack);
+	int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
+			       enum dpll_lock_status *status,
+			       struct netlink_ext_ack *extack);
+	int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
+			s32 *temp, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_ops {
+	int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     const u64 frequency,
+			     struct netlink_ext_ack *extack);
+	int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 *frequency, struct netlink_ext_ack *extack);
+	int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     const enum dpll_pin_direction direction,
+			     struct netlink_ext_ack *extack);
+	int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_pin_direction *direction,
+			     struct netlink_ext_ack *extack);
+	int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
+				const struct dpll_pin *parent_pin,
+				void *parent_pin_priv,
+				enum dpll_pin_state *state,
+				struct netlink_ext_ack *extack);
+	int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
+				 const struct dpll_device *dpll,
+				 void *dpll_priv, enum dpll_pin_state *state,
+				 struct netlink_ext_ack *extack);
+	int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
+				const struct dpll_pin *parent_pin,
+				void *parent_pin_priv,
+				const enum dpll_pin_state state,
+				struct netlink_ext_ack *extack);
+	int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
+				 const struct dpll_device *dpll,
+				 void *dpll_priv,
+				 const enum dpll_pin_state state,
+				 struct netlink_ext_ack *extack);
+	int (*prio_get)(const struct dpll_pin *pin,  void *pin_priv,
+			const struct dpll_device *dpll,  void *dpll_priv,
+			u32 *prio, struct netlink_ext_ack *extack);
+	int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			const u32 prio, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_frequency {
+	u64 min;
+	u64 max;
+};
+
+#define DPLL_PIN_FREQUENCY_RANGE(_min, _max)	\
+	{					\
+		.min = _min,			\
+		.max = _max,			\
+	}
+
+#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
+#define DPLL_PIN_FREQUENCY_1PPS \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
+#define DPLL_PIN_FREQUENCY_10MHZ \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
+#define DPLL_PIN_FREQUENCY_IRIG_B \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
+#define DPLL_PIN_FREQUENCY_DCF77 \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
+
+struct dpll_pin_properties {
+	const char *board_label;
+	const char *panel_label;
+	const char *package_label;
+	enum dpll_pin_type type;
+	unsigned long capabilities;
+	u32 freq_supported_num;
+	struct dpll_pin_frequency *freq_supported;
+};
+
+struct dpll_device
+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+
+void dpll_device_put(struct dpll_device *dpll);
+
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv);
+
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
+
+struct dpll_pin
+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+	      const struct dpll_pin_properties *prop);
+
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_put(struct dpll_pin *pin);
+
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv);
+
+#endif
-- 
2.27.0


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

* [PATCH net-next 05/11] dpll: core: Add DPLL framework base functions
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

Co-developed-by: Milena Olech <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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- remove unused mode_supported_mask from dpll_device
- fix increment refcount only if it was not already set
- remove static function comments/fix return descriptions
- add missing mutex locking on getting/registering pins
- check if dpll is registered before registering pin
- headers are squashed into this commit
- updated MAINTAINERS to add Arkadiusz and Jiri as maintainers

v8->v9:
- fix description in spdx header.
- remove refcount check if refcount was already set
- do not validate dpll ptr in dpll_device_put(..)
- fix return -ENOMEM on failed memory alloc
- do not validate pin ptr in dpll_pin_put(..)
- return -EINVAL in case of module/clock_id mismatch
- do not {} around one-line xa_for_each() macro
- move dpll_<x>_registration structs to dpll_core.c
- rephrase doc comment on device and pin id struct members
- remove ref in case of memory allocation fail
- check for required ops on pin/device registration
- mark pin with DPLL_REGISTERED once pin is registered with dpll

 MAINTAINERS              |  11 +
 drivers/Kconfig          |   2 +
 drivers/Makefile         |   1 +
 drivers/dpll/Kconfig     |   7 +
 drivers/dpll/Makefile    |   9 +
 drivers/dpll/dpll_core.c | 799 +++++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h |  90 +++++
 include/linux/dpll.h     | 136 +++++++
 8 files changed, 1055 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 include/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9a5863f1b016..54050447ec43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6304,6 +6304,17 @@ 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 SUBSYSTEM
+M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
+M:	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
+M:	Jiri Pirko <jiri@resnulli.us>
+L:	netdev@vger.kernel.org
+S:	Supported
+F:	Documentation/driver-api/dpll.rst
+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 514ae6b24cb2..ce5f63918eba 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
 
 source "drivers/cdx/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7241d80a7b29..6fea42a6dd05 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -195,3 +195,4 @@ obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
 obj-$(CONFIG_DRM_ACCEL)		+= accel/
 obj-$(CONFIG_CDX_BUS)		+= cdx/
+obj-$(CONFIG_DPLL)		+= dpll/
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
new file mode 100644
index 000000000000..a4cae73f20d3
--- /dev/null
+++ b/drivers/dpll/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic DPLL drivers configuration
+#
+
+config DPLL
+  bool
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
new file mode 100644
index 000000000000..2e5b27850110
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)      += dpll.o
+dpll-y                  += dpll_core.o
+dpll-y                  += dpll_netlink.o
+dpll-y                  += dpll_nl.o
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..18270c58e3ac
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - DPLL subsystem kernel-space interface implementation.
+ *
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel Corporation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dpll_core.h"
+
+DEFINE_MUTEX(dpll_lock);
+
+DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
+DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
+
+#define ASSERT_DPLL_REGISTERED(d)	\
+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_DPLL_NOT_REGISTERED(d)	\
+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_PIN_REGISTERED(p)	\
+	WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED))
+
+struct dpll_device_registration {
+	struct list_head list;
+	const struct dpll_device_ops *ops;
+	void *priv;
+};
+
+struct dpll_pin_registration {
+	struct list_head list;
+	const struct dpll_pin_ops *ops;
+	void *priv;
+};
+
+struct dpll_device *dpll_device_get_by_id(int id)
+{
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		return xa_load(&dpll_device_xa, id);
+
+	return NULL;
+}
+
+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;
+}
+
+static int
+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
+		    const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	bool ref_exists = false;
+	unsigned long i;
+	int ret;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+		ref_exists = true;
+		break;
+	}
+
+	if (!ref_exists) {
+		ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+		if (!ref)
+			return -ENOMEM;
+		ref->pin = pin;
+		INIT_LIST_HEAD(&ref->registration_list);
+		ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
+		}
+		refcount_set(&ref->refcount, 1);
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists) {
+			xa_erase(xa_pins, pin->pin_idx);
+			kfree(ref);
+		}
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	if (ref_exists)
+		refcount_inc(&ref->refcount);
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
+}
+
+static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
+			       const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_pins, i, ref) {
+		if (ref->pin != pin)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return -EINVAL;
+		if (refcount_dec_and_test(&ref->refcount)) {
+			list_del(&reg->list);
+			kfree(reg);
+			xa_erase(xa_pins, i);
+			WARN_ON(!list_empty(&ref->registration_list));
+			kfree(ref);
+		}
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int
+dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
+		     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	bool ref_exists = false;
+	unsigned long i;
+	int ret;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (reg) {
+			refcount_inc(&ref->refcount);
+			return 0;
+		}
+		ref_exists = true;
+		break;
+	}
+
+	if (!ref_exists) {
+		ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+		if (!ref)
+			return -ENOMEM;
+		ref->dpll = dpll;
+		INIT_LIST_HEAD(&ref->registration_list);
+		ret = xa_insert(xa_dplls, dpll->device_idx, ref, GFP_KERNEL);
+		if (ret) {
+			kfree(ref);
+			return ret;
+		}
+		refcount_set(&ref->refcount, 1);
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		if (!ref_exists) {
+			xa_erase(xa_dplls, dpll->device_idx);
+			kfree(ref);
+		}
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	if (ref_exists)
+		refcount_inc(&ref->refcount);
+	list_add_tail(&reg->list, &ref->registration_list);
+
+	return 0;
+}
+
+static void
+dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
+		     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_dplls, i, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		reg = dpll_pin_registration_find(ref, ops, priv);
+		if (WARN_ON(!reg))
+			return;
+		if (refcount_dec_and_test(&ref->refcount)) {
+			list_del(&reg->list);
+			kfree(reg);
+			xa_erase(xa_dplls, i);
+			WARN_ON(!list_empty(&ref->registration_list));
+			kfree(ref);
+		}
+		return;
+	}
+}
+
+struct dpll_pin_ref *
+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	xa_for_each(xa_refs, i, ref)
+		if (ref->dpll == dpll)
+			return ref;
+
+	return NULL;
+}
+
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i = 0;
+
+	ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
+	WARN_ON(!ref);
+	return ref;
+}
+
+static struct dpll_device *
+dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
+{
+	struct dpll_device *dpll;
+	int ret;
+
+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+	if (!dpll)
+		return ERR_PTR(-ENOMEM);
+	refcount_set(&dpll->refcount, 1);
+	INIT_LIST_HEAD(&dpll->registration_list);
+	dpll->device_idx = device_idx;
+	dpll->clock_id = clock_id;
+	dpll->module = module;
+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b,
+		       GFP_KERNEL);
+	if (ret) {
+		kfree(dpll);
+		return ERR_PTR(ret);
+	}
+	xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
+
+	return dpll;
+}
+
+/**
+ * dpll_device_get - find existing or create new dpll device
+ * @clock_id: clock_id of creator
+ * @device_idx: idx given by device driver
+ * @module: reference to registering module
+ *
+ * Get existing object of a dpll device, unique for given arguments.
+ * Create new if doesn't exist yet.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR(X) - error
+ */
+struct dpll_device *
+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_lock);
+	xa_for_each(&dpll_device_xa, index, dpll) {
+		if (dpll->clock_id == clock_id &&
+		    dpll->device_idx == device_idx &&
+		    dpll->module == module) {
+			ret = dpll;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_device_alloc(clock_id, device_idx, module);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_get);
+
+/**
+ * dpll_device_put - decrease the refcount and free memory if possible
+ * @dpll: dpll_device struct pointer
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Drop reference for a dpll device, if all references are gone, delete
+ * dpll device object.
+ */
+void dpll_device_put(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll_lock);
+	if (refcount_dec_and_test(&dpll->refcount)) {
+		ASSERT_DPLL_NOT_REGISTERED(dpll);
+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+		xa_destroy(&dpll->pin_refs);
+		xa_erase(&dpll_device_xa, dpll->id);
+		WARN_ON(!list_empty(&dpll->registration_list));
+		kfree(dpll);
+	}
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_put);
+
+static struct dpll_device_registration *
+dpll_device_registration_find(struct dpll_device *dpll,
+			      const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	list_for_each_entry(reg, &dpll->registration_list, list) {
+		if (reg->ops == ops && reg->priv == priv)
+			return reg;
+	}
+	return NULL;
+}
+
+/**
+ * dpll_device_register - register the dpll device in the subsystem
+ * @dpll: pointer to a dpll
+ * @type: type of a dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Make dpll device available for user space.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+	bool first_registration = false;
+
+	if (WARN_ON(!ops))
+		return -EINVAL;
+	if (WARN_ON(!ops->mode_get))
+		return -EINVAL;
+	if (WARN_ON(!ops->lock_status_get))
+		return -EINVAL;
+	if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (reg) {
+		mutex_unlock(&dpll_lock);
+		return -EEXIST;
+	}
+
+	reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+	if (!reg) {
+		mutex_unlock(&dpll_lock);
+		return -ENOMEM;
+	}
+	reg->ops = ops;
+	reg->priv = priv;
+	dpll->type = type;
+	first_registration = list_empty(&dpll->registration_list);
+	list_add_tail(&reg->list, &dpll->registration_list);
+	if (!first_registration) {
+		mutex_unlock(&dpll_lock);
+		return 0;
+	}
+
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_lock);
+	dpll_device_create_ntf(dpll);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+/**
+ * dpll_device_unregister - unregister dpll device
+ * @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Unregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv)
+{
+	struct dpll_device_registration *reg;
+
+	mutex_lock(&dpll_lock);
+	ASSERT_DPLL_REGISTERED(dpll);
+	dpll_device_delete_ntf(dpll);
+	reg = dpll_device_registration_find(dpll, ops, priv);
+	if (WARN_ON(!reg)) {
+		mutex_unlock(&dpll_lock);
+		return;
+	}
+	list_del(&reg->list);
+	kfree(reg);
+
+	if (!list_empty(&dpll->registration_list)) {
+		mutex_unlock(&dpll_lock);
+		return;
+	}
+	xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_unregister);
+
+static struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
+	       const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	pin->pin_idx = pin_idx;
+	pin->clock_id = clock_id;
+	pin->module = module;
+	if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
+		    prop->type > DPLL_PIN_TYPE_MAX)) {
+		ret = -EINVAL;
+		goto err;
+	}
+	pin->prop = prop;
+	refcount_set(&pin->refcount, 1);
+	xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
+	xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
+	ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
+	if (ret)
+		goto err;
+	return pin;
+err:
+	xa_destroy(&pin->dpll_refs);
+	xa_destroy(&pin->parent_refs);
+	kfree(pin);
+	return ERR_PTR(ret);
+}
+
+/**
+ * dpll_pin_get - find existing or create new dpll pin
+ * @clock_id: clock_id of creator
+ * @pin_idx: idx given by dev driver
+ * @module: reference to registering module
+ * @prop: dpll pin properties
+ *
+ * Get existing object of a pin (unique for given arguments) or create new
+ * if doesn't exist yet.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid allocated dpll_pin struct pointer if succeeded
+ * * ERR_PTR(X) - error
+ */
+struct dpll_pin *
+dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
+	     const struct dpll_pin_properties *prop)
+{
+	struct dpll_pin *pos, *ret = NULL;
+	unsigned long i;
+
+	mutex_lock(&dpll_lock);
+	xa_for_each(&dpll_pin_xa, i, pos) {
+		if (pos->clock_id == clock_id &&
+		    pos->pin_idx == pin_idx &&
+		    pos->module == module) {
+			ret = pos;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	if (!ret)
+		ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_get);
+
+/**
+ * dpll_pin_put - decrease the refcount and free memory if possible
+ * @pin: pointer to a pin to be put
+ *
+ * Drop reference for a pin, if all references are gone, delete pin object.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_pin_put(struct dpll_pin *pin)
+{
+	mutex_lock(&dpll_lock);
+	if (refcount_dec_and_test(&pin->refcount)) {
+		xa_destroy(&pin->dpll_refs);
+		xa_destroy(&pin->parent_refs);
+		xa_erase(&dpll_pin_xa, pin->id);
+		kfree(pin);
+	}
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_put);
+
+static int
+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		    const struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
+	if (ret)
+		return ret;
+	ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
+	if (ret)
+		goto ref_pin_del;
+	xa_set_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
+	dpll_pin_create_ntf(pin);
+
+	return ret;
+
+ref_pin_del:
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+	return ret;
+}
+
+/**
+ * dpll_pin_register - register the dpll pin in the subsystem
+ * @dpll: pointer to a dpll
+ * @pin: pointer to a dpll pin
+ * @ops: ops for a dpll pin ops
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int
+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		  const struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	if (WARN_ON(!ops) ||
+	    WARN_ON(!ops->state_on_dpll_get) ||
+	    WARN_ON(!ops->direction_get))
+		return -EINVAL;
+	if (ASSERT_DPLL_REGISTERED(dpll))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	if (WARN_ON(!(dpll->module == pin->module &&
+		      dpll->clock_id == pin->clock_id)))
+		ret = -EINVAL;
+	else
+		ret = __dpll_pin_register(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+static void
+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv)
+{
+	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
+	if (xa_empty(&pin->dpll_refs))
+		xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED);
+}
+
+/**
+ * dpll_pin_unregister - unregister dpll pin from dpll device
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv)
+{
+	if (WARN_ON(xa_empty(&dpll->pin_refs)))
+		return;
+	if (WARN_ON(!xa_empty(&pin->parent_refs)))
+		return;
+
+	mutex_lock(&dpll_lock);
+	dpll_pin_delete_ntf(pin);
+	__dpll_pin_unregister(dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_unregister);
+
+/**
+ * dpll_pin_on_pin_register - register a pin with a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Register a pin with a parent pin, create references between them and
+ * between newly registered pin and dplls connected with a parent pin.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * negative - error value
+ */
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i, stop;
+	int ret;
+
+	if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
+		return -EINVAL;
+
+	if (WARN_ON(!ops) ||
+	    WARN_ON(!ops->state_on_pin_get) ||
+	    WARN_ON(!ops->direction_get))
+		return -EINVAL;
+	if (ASSERT_PIN_REGISTERED(parent))
+		return -EINVAL;
+
+	mutex_lock(&dpll_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) {
+		ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
+		if (ret) {
+			stop = i;
+			goto dpll_unregister;
+		}
+		dpll_pin_create_ntf(pin);
+	}
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+
+dpll_unregister:
+	xa_for_each(&parent->dpll_refs, i, ref)
+		if (i < stop) {
+			dpll_pin_delete_ntf(pin);
+			__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+		}
+	refcount_dec(&pin->refcount);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+unlock:
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
+
+/**
+ * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Note: It does not free the memory
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+
+	mutex_lock(&dpll_lock);
+	dpll_pin_delete_ntf(pin);
+	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+	refcount_dec(&pin->refcount);
+	xa_for_each(&pin->dpll_refs, i, ref)
+		__dpll_pin_unregister(ref->dpll, pin, ops, priv);
+	mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+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;
+}
+
+void *dpll_priv(struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first(dpll);
+	return reg->priv;
+}
+
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
+{
+	struct dpll_device_registration *reg;
+
+	reg = dpll_device_registration_first(dpll);
+	return reg->ops;
+}
+
+static struct dpll_pin_registration *
+dpll_pin_registration_first(struct dpll_pin_ref *ref)
+{
+	struct dpll_pin_registration *reg;
+
+	reg = list_first_entry_or_null(&ref->registration_list,
+				       struct dpll_pin_registration, list);
+	WARN_ON(!reg);
+	return reg;
+}
+
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
+			    struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load(&dpll->pin_refs, pin->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
+			   struct dpll_pin *pin)
+{
+	struct dpll_pin_registration *reg;
+	struct dpll_pin_ref *ref;
+
+	ref = xa_load(&pin->parent_refs, parent->pin_idx);
+	if (!ref)
+		return NULL;
+	reg = dpll_pin_registration_first(ref);
+	return reg->priv;
+}
+
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
+{
+	struct dpll_pin_registration *reg;
+
+	reg = dpll_pin_registration_first(ref);
+	return reg->ops;
+}
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	return 0;
+
+error:
+	mutex_destroy(&dpll_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..3574bebf2c63
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_CORE_H__
+#define __DPLL_CORE_H__
+
+#include <linux/dpll.h>
+#include <linux/list.h>
+#include <linux/refcount.h>
+#include "dpll_netlink.h"
+
+#define DPLL_REGISTERED		XA_MARK_1
+
+/**
+ * struct dpll_device - stores DPLL device internal data
+ * @id:			unique id number for device given by dpll subsystem
+ * @device_idx:		id given by dev driver
+ * @clock_id:		unique identifier (clock_id) of a dpll
+ * @module:		module of creator
+ * @type:		type of a dpll
+ * @pin_refs:		stores pins registered within a dpll
+ * @refcount:		refcount
+ * @registration_list:	list of registered ops and priv data of dpll owners
+ **/
+struct dpll_device {
+	u32 id;
+	u32 device_idx;
+	u64 clock_id;
+	struct module *module;
+	enum dpll_type type;
+	struct xarray pin_refs;
+	refcount_t refcount;
+	struct list_head registration_list;
+};
+
+/**
+ * struct dpll_pin - structure for a dpll pin
+ * @id:			unique id number for pin given by dpll subsystem
+ * @pin_idx:		index of a pin given by dev driver
+ * @clock_id:		clock_id of creator
+ * @module:		module of creator
+ * @dpll_refs:		hold referencees to dplls pin was registered with
+ * @parent_refs:	hold references to parent pins pin was registered with
+ * @prop:		pointer to pin properties given by registerer
+ * @rclk_dev_name:	holds name of device when pin can recover clock from it
+ * @refcount:		refcount
+ **/
+struct dpll_pin {
+	u32 id;
+	u32 pin_idx;
+	u64 clock_id;
+	struct module *module;
+	struct xarray dpll_refs;
+	struct xarray parent_refs;
+	const struct dpll_pin_properties *prop;
+	char *rclk_dev_name;
+	refcount_t refcount;
+};
+
+/**
+ * struct dpll_pin_ref - structure for referencing either dpll or pins
+ * @dpll:		pointer to a dpll
+ * @pin:		pointer to a pin
+ * @registration_list:	list of ops and priv data registered with the ref
+ * @refcount:		refcount
+ **/
+struct dpll_pin_ref {
+	union {
+		struct dpll_device *dpll;
+		struct dpll_pin *pin;
+	};
+	struct list_head registration_list;
+	refcount_t refcount;
+};
+
+void *dpll_priv(struct dpll_device *dpll);
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
+
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
+struct dpll_device *dpll_device_get_by_id(int id);
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
+extern struct xarray dpll_device_xa;
+extern struct xarray dpll_pin_xa;
+extern struct mutex dpll_lock;
+#endif
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..d3258205e499
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_H__
+#define __DPLL_H__
+
+#include <uapi/linux/dpll.h>
+#include <linux/device.h>
+#include <linux/netlink.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
+			enum dpll_mode *mode, struct netlink_ext_ack *extack);
+	int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
+			const enum dpll_mode mode,
+			struct netlink_ext_ack *extack);
+	bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
+			       const enum dpll_mode mode,
+			       struct netlink_ext_ack *extack);
+	int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
+			       enum dpll_lock_status *status,
+			       struct netlink_ext_ack *extack);
+	int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
+			s32 *temp, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_ops {
+	int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     const u64 frequency,
+			     struct netlink_ext_ack *extack);
+	int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 *frequency, struct netlink_ext_ack *extack);
+	int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     const enum dpll_pin_direction direction,
+			     struct netlink_ext_ack *extack);
+	int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_pin_direction *direction,
+			     struct netlink_ext_ack *extack);
+	int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
+				const struct dpll_pin *parent_pin,
+				void *parent_pin_priv,
+				enum dpll_pin_state *state,
+				struct netlink_ext_ack *extack);
+	int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
+				 const struct dpll_device *dpll,
+				 void *dpll_priv, enum dpll_pin_state *state,
+				 struct netlink_ext_ack *extack);
+	int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
+				const struct dpll_pin *parent_pin,
+				void *parent_pin_priv,
+				const enum dpll_pin_state state,
+				struct netlink_ext_ack *extack);
+	int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
+				 const struct dpll_device *dpll,
+				 void *dpll_priv,
+				 const enum dpll_pin_state state,
+				 struct netlink_ext_ack *extack);
+	int (*prio_get)(const struct dpll_pin *pin,  void *pin_priv,
+			const struct dpll_device *dpll,  void *dpll_priv,
+			u32 *prio, struct netlink_ext_ack *extack);
+	int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			const u32 prio, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_frequency {
+	u64 min;
+	u64 max;
+};
+
+#define DPLL_PIN_FREQUENCY_RANGE(_min, _max)	\
+	{					\
+		.min = _min,			\
+		.max = _max,			\
+	}
+
+#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
+#define DPLL_PIN_FREQUENCY_1PPS \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
+#define DPLL_PIN_FREQUENCY_10MHZ \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
+#define DPLL_PIN_FREQUENCY_IRIG_B \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
+#define DPLL_PIN_FREQUENCY_DCF77 \
+	DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
+
+struct dpll_pin_properties {
+	const char *board_label;
+	const char *panel_label;
+	const char *package_label;
+	enum dpll_pin_type type;
+	unsigned long capabilities;
+	u32 freq_supported_num;
+	struct dpll_pin_frequency *freq_supported;
+};
+
+struct dpll_device
+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+
+void dpll_device_put(struct dpll_device *dpll);
+
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+			 const struct dpll_device_ops *ops, void *priv);
+
+void dpll_device_unregister(struct dpll_device *dpll,
+			    const struct dpll_device_ops *ops, void *priv);
+
+struct dpll_pin
+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+	      const struct dpll_pin_properties *prop);
+
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+			 const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_put(struct dpll_pin *pin);
+
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+			     const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+				const struct dpll_pin_ops *ops, void *priv);
+
+#endif
-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 06/11] dpll: netlink: Add DPLL framework base functions
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

Co-developed-by: Milena Olech <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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- fix DPLL_MODE_SUPPORTED attribute in device-get response
- remove dead code from dpll_msg_add_pin_freq(..)
- don't fill dpll device for muxed pins
- API headers are added to this commit

v8->v9:
- fix pin-id-get/device-id-get behavior
- reshuffle order of functions
- avoid forward declarations
- functions for adding pin/device handle next to each other
- pass ops callback return values to the user
- remove dpll_cmd_pin_fill_details(..) function, merge the code into
  __dpll_cmd_pin_dump_one(..)
- rename __dpll_cmd_pin_dump_one() to dpll_cmd_pin_get_one()
- use WARN_ON macro when dpll ref is missing
- remove redundant pin's dpll list not empty check
- remove double spaces inside if statement
- add extack message when set command is not possible
- do not return error when callback is not required
- WARN_ON missing ops moved to dpll_core.c
- use DPLL_REGISTERED if pin was registered with dpll
- fix pin-id-get return and add extack errors
- fix device-id-get return and add extack errors
- drop pointless init of variables
- add macro for iterating over marked pins/devices
- move dpll_set_from_nlattr() for consistent order
- use GENL_REQ_ATTR_CHECK() for checking attibute presence
- fill extack if pin/device was not found
- drop pointless init of variables
- WARN_ON if dpll not registered on send event
- rename goto labels to indicate error path
- fix docs
- drop pointless init of variables
- verify pin in notify with a mark
- prevent ops->mode_set call if missing callback
- move static dpll_msg_add_pin_handle() from pin<->netdev patch
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple

 drivers/dpll/dpll_netlink.c | 1273 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   17 +
 include/linux/dpll.h        |    4 +
 3 files changed, 1294 insertions(+)
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..c44dda78737d
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,1273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include "dpll_core.h"
+#include "dpll_nl.h"
+#include <uapi/linux/dpll.h>
+
+#define ASSERT_NOT_NULL(ptr)	(WARN_ON(!ptr))
+
+#define xa_for_each_marked_start(xa, index, entry, filter, start) \
+	for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \
+	     entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))
+
+struct dpll_dump_ctx {
+	unsigned long idx;
+};
+
+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
+{
+	return (struct dpll_dump_ctx *)cb->ctx;
+}
+
+static int
+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+/**
+ * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
+ * @msg: pointer to sk_buff message to attach a pin handle
+ * @pin: pin pointer
+ *
+ * Return:
+ * * 0 - success
+ * * -EMSGSIZE - no space in message to attach pin handle
+ */
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	if (!pin)
+		return 0;
+	if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static int
+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	enum dpll_mode mode;
+	int ret;
+
+	ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_mode_supported(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 (!ops->mode_supported)
+		return 0;
+	for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
+		if (ops->mode_supported(dpll, dpll_priv(dpll), mode, extack))
+			if (nla_put_u8(msg, DPLL_A_MODE_SUPPORTED, mode))
+				return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	enum dpll_lock_status status;
+	int ret;
+
+	ret = ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	s32 temp;
+	int ret;
+
+	if (!ops->temp_get)
+		return 0;
+	ret = ops->temp_get(dpll, dpll_priv(dpll), &temp, extack);
+	if (ret)
+		return ret;
+	if (nla_put_s32(msg, DPLL_A_TEMP, temp))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(struct sk_buff *msg, struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref,
+		      struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	u32 prio;
+	int ret;
+
+	if (!ops->prio_get)
+		return 0;
+	ret = ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+			    dpll_priv(dpll), &prio, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
+			       struct dpll_pin_ref *ref,
+			       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	enum dpll_pin_state state;
+	int ret;
+
+
+	if (!ops->state_on_dpll_get)
+		return 0;
+	ret = ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				     dpll, dpll_priv(dpll), &state, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
+			   struct dpll_pin_ref *ref,
+			   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	enum dpll_pin_direction direction;
+	int ret;
+
+	ret = ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+				 dpll_priv(dpll), &direction, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	struct nlattr *nest;
+	int fs, ret;
+	u64 freq;
+
+	if (!ops->frequency_get)
+		return 0;
+	ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+				 dpll_priv(dpll), &freq, extack);
+	if (ret)
+		return ret;
+	if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
+		return -EMSGSIZE;
+	for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
+		nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
+		if (!nest)
+			return -EMSGSIZE;
+		freq = pin->prop->freq_supported[fs].min;
+		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
+				   &freq, 0)) {
+			nla_nest_cancel(msg, nest);
+			return -EMSGSIZE;
+		}
+		freq = pin->prop->freq_supported[fs].max;
+		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
+				   &freq, 0)) {
+			nla_nest_cancel(msg, nest);
+			return -EMSGSIZE;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+}
+
+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
+{
+	int fs;
+
+	for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
+		if (freq >= pin->prop->freq_supported[fs].min &&
+		    freq <= pin->prop->freq_supported[fs].max)
+			return true;
+	return false;
+}
+
+static int
+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct dpll_pin_ref *dpll_ref,
+			 struct netlink_ext_ack *extack)
+{
+	enum dpll_pin_state state;
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *ppin;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref) {
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+		void *parent_priv;
+
+		ppin = ref->pin;
+		parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
+		ret = ops->state_on_pin_get(pin,
+					    dpll_pin_on_pin_priv(ppin, pin),
+					    ppin, parent_priv, &state, extack);
+		if (ret)
+			return ret;
+		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
+		if (!nest)
+			return -EMSGSIZE;
+		if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
+		       struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	struct nlattr *attr;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->dpll_refs, index, ref) {
+		attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_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)
+			goto nest_cancel;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
+		if (ret)
+			goto nest_cancel;
+		ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
+		if (ret)
+			goto nest_cancel;
+		nla_nest_end(msg, attr);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_end(msg, attr);
+	return ret;
+}
+
+static int
+dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
+			struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_properties *prop = pin->prop;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ASSERT_NOT_NULL(ref);
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
+	if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin->module)))
+		return -EMSGSIZE;
+	if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
+			  &pin->clock_id, 0))
+		return -EMSGSIZE;
+	if (prop->board_label &&
+	    nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
+		return -EMSGSIZE;
+	if (prop->panel_label &&
+	    nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
+		return -EMSGSIZE;
+	if (prop->package_label &&
+	    nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
+			   prop->package_label))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
+		return -EMSGSIZE;
+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop->capabilities))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
+	if (ret)
+		return ret;
+	if (xa_empty(&pin->parent_refs))
+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
+	else
+		ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
+
+	return ret;
+}
+
+static int
+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
+		    struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	ret = dpll_msg_add_dev_handle(msg, dpll);
+	if (ret)
+		return ret;
+	if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
+		return -EMSGSIZE;
+	if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_temp(msg, dpll, extack);
+	if (ret)
+		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;
+	ret = dpll_msg_add_mode_supported(msg, dpll, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
+		return -EMSGSIZE;
+
+	return ret;
+}
+
+static int
+dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int ret;
+
+	if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))
+		return -ENODEV;
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+	if (!hdr)
+		goto err_free_msg;
+	ret = dpll_device_get_one(dpll, msg, NULL);
+	if (ret)
+		goto err_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+err_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+err_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_device_create_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
+}
+
+int dpll_device_delete_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
+}
+
+int __dpll_device_change_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
+}
+
+/**
+ * dpll_device_change_ntf - notify that the dpll device has been changed
+ * @dpll: registered dpll pointer
+ *
+ * Context: acquires and holds a dpll_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_device_change_ntf(struct dpll_device *dpll)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = __dpll_device_change_ntf(dpll);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
+
+static int
+dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int ret;
+
+	if (WARN_ON(!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)))
+		return -ENODEV;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+	if (!hdr)
+		goto err_free_msg;
+	ret = dpll_cmd_pin_get_one(msg, pin, NULL);
+	if (ret)
+		goto err_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+err_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+err_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_pin_create_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
+}
+
+int dpll_pin_delete_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
+}
+
+static int __dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
+}
+
+/**
+ * dpll_pin_change_ntf - notify that the pin has been changed
+ * @pin: registered pin pointer
+ *
+ * Context: acquires and holds a dpll_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = __dpll_pin_change_ntf(pin);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
+
+static int
+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
+		  struct netlink_ext_ack *extack)
+{
+	u64 freq = nla_get_u64(a);
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	int ret;
+
+	if (!dpll_pin_is_freq_supported(pin, freq)) {
+		NL_SET_ERR_MSG_FMT(extack, "not supported freq:%llu on pin:%u",
+				   freq, pin->id);
+		return -EINVAL;
+	}
+
+	xa_for_each(&pin->dpll_refs, i, ref) {
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+		struct dpll_device *dpll = ref->dpll;
+
+		if (!ops->frequency_set)
+			return -EOPNOTSUPP;
+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+					 dpll, dpll_priv(dpll), freq, extack);
+		if (ret)
+			return ret;
+		__dpll_pin_change_ntf(pin);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
+			  enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *parent_ref;
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *dpll_ref;
+	void *pin_priv, *parent_priv;
+	struct dpll_pin *parent;
+	unsigned long i;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change state",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	parent = xa_load(&dpll_pin_xa, parent_idx);
+	if (!parent)
+		return -EINVAL;
+	parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
+	if (!parent_ref)
+		return -EINVAL;
+	xa_for_each(&parent->dpll_refs, i, dpll_ref) {
+		ops = dpll_pin_ops(parent_ref);
+		if (!ops->state_on_pin_set)
+			return -EOPNOTSUPP;
+		pin_priv = dpll_pin_on_pin_priv(parent, pin);
+		parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, parent);
+		ret = ops->state_on_pin_set(pin, pin_priv, parent, parent_priv,
+					    state, extack);
+		if (ret)
+			return ret;
+	}
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		   enum dpll_pin_state state,
+		   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change state",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_dpll_set)
+		return -EOPNOTSUPP;
+	ret = ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				     dpll, dpll_priv(dpll), state, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		  u32 prio, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change prio",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->prio_set)
+		return -EOPNOTSUPP;
+	ret = ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+			    dpll_priv(dpll), prio, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
+		       enum dpll_pin_direction direction,
+		       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "pin:%u not allowed to change direction",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->direction_set)
+		return -EOPNOTSUPP;
+	ret = ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				 dpll, dpll_priv(dpll), direction, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
+			   struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	enum dpll_pin_direction direction;
+	enum dpll_pin_state state;
+	struct dpll_pin_ref *ref;
+	struct dpll_device *dpll;
+	u32 pdpll_idx, prio;
+	int ret;
+
+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
+			 NULL, extack);
+	if (!tb[DPLL_A_ID]) {
+		NL_SET_ERR_MSG(extack, "device parent id expected");
+		return -EINVAL;
+	}
+	pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
+	dpll = xa_load(&dpll_device_xa, pdpll_idx);
+	if (!dpll)
+		return -EINVAL;
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	if (tb[DPLL_A_PIN_STATE]) {
+		state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+		ret = dpll_pin_state_set(dpll, pin, state, extack);
+		if (ret)
+			return ret;
+	}
+	if (tb[DPLL_A_PIN_PRIO]) {
+		prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
+		ret = dpll_pin_prio_set(dpll, pin, prio, extack);
+		if (ret)
+			return ret;
+	}
+	if (tb[DPLL_A_PIN_DIRECTION]) {
+		direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
+		ret = dpll_pin_direction_set(pin, dpll, direction, extack);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int
+dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
+			struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	enum dpll_pin_state state;
+	u32 ppin_idx;
+	int ret;
+
+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
+			 NULL, extack);
+	if (!tb[DPLL_A_PIN_ID]) {
+		NL_SET_ERR_MSG(extack, "parent pin id expected");
+		return -EINVAL;
+	}
+	ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
+	state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+	ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
+{
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLL_A_PIN_FREQUENCY:
+			ret = dpll_pin_freq_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_DEVICE:
+			ret = dpll_pin_parent_device_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_PIN:
+			ret = dpll_pin_parent_pin_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_ID:
+		case DPLL_A_ID:
+			break;
+		default:
+			NL_SET_ERR_MSG_FMT(info->extack,
+					   "unsupported attribute (%d)",
+					   nla_type(a));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static struct dpll_pin *
+dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
+	      enum dpll_pin_type type, struct nlattr *board_label,
+	      struct nlattr *panel_label, struct nlattr *package_label,
+	      struct netlink_ext_ack *extack)
+{
+	bool board_match, panel_match, package_match;
+	struct dpll_pin *pin_match = NULL, *pin;
+	const struct dpll_pin_properties *prop;
+	bool cid_match, mod_match, type_match;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {
+		prop = pin->prop;
+		cid_match = clock_id ? pin->clock_id == clock_id : true;
+		mod_match = mod_name_attr && module_name(pin->module) ?
+			!nla_strcmp(mod_name_attr,
+				    module_name(pin->module)) : true;
+		type_match = type ? prop->type == type : true;
+		board_match = board_label ? (prop->board_label ?
+			!nla_strcmp(board_label, prop->board_label) : false) :
+			true;
+		panel_match = panel_label ? (prop->panel_label ?
+			!nla_strcmp(panel_label, prop->panel_label) : false) :
+			true;
+		package_match = package_label ? (prop->package_label ?
+			!nla_strcmp(package_label, prop->package_label) :
+			false) : true;
+		if (cid_match && mod_match && type_match && board_match &&
+		    panel_match && package_match) {
+			if (pin_match) {
+				NL_SET_ERR_MSG(extack, "multiple matches");
+				return ERR_PTR(-EINVAL);
+			}
+			pin_match = pin;
+		};
+	}
+	if (!pin_match) {
+		NL_SET_ERR_MSG(extack, "not found");
+		return ERR_PTR(-ENODEV);
+	}
+	return pin_match;
+}
+
+static struct dpll_pin *dpll_pin_find_from_nlattr(struct genl_info *info)
+{
+	struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
+		*panel_label_attr = NULL, *package_label_attr = NULL;
+	enum dpll_pin_type type = 0;
+	u64 clock_id = 0;
+	int rem = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_CLOCK_ID:
+			if (clock_id)
+				goto duplicated_attr;
+			clock_id = nla_get_u64(attr);
+			break;
+		case DPLL_A_MODULE_NAME:
+			if (mod_name_attr)
+				goto duplicated_attr;
+			mod_name_attr = attr;
+			break;
+		case DPLL_A_PIN_TYPE:
+			if (type)
+				goto duplicated_attr;
+			type = nla_get_u8(attr);
+		break;
+		case DPLL_A_PIN_BOARD_LABEL:
+			if (board_label_attr)
+				goto duplicated_attr;
+			board_label_attr = attr;
+		break;
+		case DPLL_A_PIN_PANEL_LABEL:
+			if (panel_label_attr)
+				goto duplicated_attr;
+			panel_label_attr = attr;
+		break;
+		case DPLL_A_PIN_PACKAGE_LABEL:
+			if (package_label_attr)
+				goto duplicated_attr;
+			package_label_attr = attr;
+		break;
+		default:
+			break;
+		}
+	}
+	if (!(clock_id  || mod_name_attr || board_label_attr ||
+	      panel_label_attr || package_label_attr)) {
+		NL_SET_ERR_MSG(info->extack, "missing attributes");
+		return ERR_PTR(-EINVAL);
+	}
+	return dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
+			     panel_label_attr, package_label_attr,
+			     info->extack);
+duplicated_attr:
+	NL_SET_ERR_MSG(info->extack, "duplicated attribute");
+	return ERR_PTR(-EINVAL);
+}
+
+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin;
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_PIN_ID_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	pin = dpll_pin_find_from_nlattr(info);
+	if (!IS_ERR(pin)) {
+		ret = dpll_msg_add_pin_handle(msg, pin);
+		if (ret) {
+			nlmsg_free(msg);
+			return ret;
+		}
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[0];
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	if (!pin)
+		return -ENODEV;
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_PIN_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+	ret = dpll_cmd_pin_get_one(msg, pin, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_pin *pin;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret = 0;
+
+	xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,
+				 ctx->idx) {
+		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+				  cb->nlh->nlmsg_seq,
+				  &dpll_nl_family, NLM_F_MULTI,
+				  DPLL_CMD_PIN_GET);
+		if (!hdr) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);
+		if (ret) {
+			genlmsg_cancel(skb, hdr);
+			break;
+		}
+		genlmsg_end(skb, hdr);
+	}
+	if (ret == -EMSGSIZE) {
+		ctx->idx = i;
+		return skb->len;
+	}
+	return ret;
+}
+
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[0];
+
+	return dpll_pin_set_from_nlattr(pin, info);
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	int ret;
+
+	nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
+		  genlmsg_len(info->genlhdr), NULL, info->extack);
+	if (tb[DPLL_A_MODE]) {
+		if (!ops->mode_set) {
+			NL_SET_ERR_MSG(info->extack, "mode set not supported");
+			return -EINVAL;
+		}
+		ret = ops->mode_set(dpll, dpll_priv(dpll),
+				    nla_get_u8(tb[DPLL_A_MODE]), info->extack);
+		if (ret)
+			return ret;
+		__dpll_device_change_ntf(dpll);
+	}
+
+	return 0;
+}
+
+static struct dpll_device *
+dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
+		 enum dpll_type type, struct netlink_ext_ack *extack)
+{
+	struct dpll_device *dpll_match = NULL, *dpll;
+	bool cid_match, mod_match, type_match;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		cid_match = clock_id ? dpll->clock_id == clock_id : true;
+		mod_match = mod_name_attr ? (module_name(dpll->module) ?
+			!nla_strcmp(mod_name_attr,
+				    module_name(dpll->module)) : false) : true;
+		type_match = type ? dpll->type == type : true;
+		if (cid_match && mod_match && type_match) {
+			if (dpll_match) {
+				NL_SET_ERR_MSG(extack, "multiple matches");
+				return ERR_PTR(-EINVAL);
+			}
+			dpll_match = dpll;
+		}
+	}
+	if (!dpll_match) {
+		NL_SET_ERR_MSG(extack, "not found");
+		return ERR_PTR(-ENODEV);
+	}
+
+	return dpll_match;
+}
+
+static struct dpll_device *
+dpll_device_find_from_nlattr(struct genl_info *info)
+{
+	struct nlattr *attr, *mod_name_attr = NULL;
+	enum dpll_type type = 0;
+	u64 clock_id = 0;
+	int rem = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_CLOCK_ID:
+			if (clock_id)
+				goto duplicated_attr;
+			clock_id = nla_get_u64(attr);
+			break;
+		case DPLL_A_MODULE_NAME:
+			if (mod_name_attr)
+				goto duplicated_attr;
+			mod_name_attr = attr;
+			break;
+		case DPLL_A_TYPE:
+			if (type)
+				goto duplicated_attr;
+			type = nla_get_u8(attr);
+			break;
+		default:
+			break;
+		}
+	}
+	if (!clock_id && !mod_name_attr && !type) {
+		NL_SET_ERR_MSG(info->extack, "missing attributes");
+		return ERR_PTR(-EINVAL);
+	}
+	return dpll_device_find(clock_id, mod_name_attr, type, info->extack);
+duplicated_attr:
+	NL_SET_ERR_MSG(info->extack, "duplicated attribute");
+	return ERR_PTR(-EINVAL);
+}
+
+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll;
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_DEVICE_ID_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	dpll = dpll_device_find_from_nlattr(info);
+	if (!IS_ERR(dpll)) {
+		ret = dpll_msg_add_dev_handle(msg, dpll);
+		if (ret) {
+			nlmsg_free(msg);
+			return ret;
+		}
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	ret = dpll_device_get_one(dpll, msg, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+
+	return dpll_set_from_nlattr(dpll, info);
+}
+
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_device *dpll;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret = 0;
+
+	xa_for_each_marked_start(&dpll_device_xa, i, dpll, DPLL_REGISTERED,
+				 ctx->idx) {
+		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+				  cb->nlh->nlmsg_seq, &dpll_nl_family,
+				  NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
+		if (!hdr) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_device_get_one(dpll, skb, cb->extack);
+		if (ret) {
+			genlmsg_cancel(skb, hdr);
+			break;
+		}
+		genlmsg_end(skb, hdr);
+	}
+	if (ret == -EMSGSIZE) {
+		ctx->idx = i;
+		return skb->len;
+	}
+	return ret;
+}
+
+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		  struct genl_info *info)
+{
+	u32 id;
+
+	if (GENL_REQ_ATTR_CHECK(info, DPLL_A_ID))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	id = nla_get_u32(info->attrs[DPLL_A_ID]);
+	info->user_ptr[0] = dpll_device_get_by_id(id);
+	if (!info->user_ptr[0]) {
+		NL_SET_ERR_MSG(info->extack, "device not found");
+		goto unlock;
+	}
+	return 0;
+unlock:
+	mutex_unlock(&dpll_lock);
+	return -ENODEV;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+int
+dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		     struct genl_info *info)
+{
+	mutex_lock(&dpll_lock);
+
+	return 0;
+}
+
+void
+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		   struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+int dpll_lock_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_lock);
+
+	return 0;
+}
+
+int dpll_unlock_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_lock);
+
+	return 0;
+}
+
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		      struct genl_info *info)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	if (GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID)) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	info->user_ptr[0] = xa_load(&dpll_pin_xa,
+				    nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
+	if (!info->user_ptr[0]) {
+		NL_SET_ERR_MSG(info->extack, "pin not found");
+		ret = -ENODEV;
+		goto unlock_dev;
+	}
+
+	return 0;
+
+unlock_dev:
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+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..2e3a6b5984e4
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+int dpll_device_create_ntf(struct dpll_device *dpll);
+
+int dpll_device_delete_ntf(struct dpll_device *dpll);
+
+int dpll_pin_create_ntf(struct dpll_pin *pin);
+
+int dpll_pin_delete_ntf(struct dpll_pin *pin);
+
+int __init dpll_netlink_init(void);
+
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index d3258205e499..a96fddbc5279 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -133,4 +133,8 @@ int dpll_pin_on_pin_register(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);
 
+int dpll_device_change_ntf(struct dpll_device *dpll);
+
+int dpll_pin_change_ntf(struct dpll_pin *pin);
+
 #endif
-- 
2.27.0


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

* [PATCH net-next 06/11] dpll: netlink: Add DPLL framework base functions
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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

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

Co-developed-by: Milena Olech <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: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- fix DPLL_MODE_SUPPORTED attribute in device-get response
- remove dead code from dpll_msg_add_pin_freq(..)
- don't fill dpll device for muxed pins
- API headers are added to this commit

v8->v9:
- fix pin-id-get/device-id-get behavior
- reshuffle order of functions
- avoid forward declarations
- functions for adding pin/device handle next to each other
- pass ops callback return values to the user
- remove dpll_cmd_pin_fill_details(..) function, merge the code into
  __dpll_cmd_pin_dump_one(..)
- rename __dpll_cmd_pin_dump_one() to dpll_cmd_pin_get_one()
- use WARN_ON macro when dpll ref is missing
- remove redundant pin's dpll list not empty check
- remove double spaces inside if statement
- add extack message when set command is not possible
- do not return error when callback is not required
- WARN_ON missing ops moved to dpll_core.c
- use DPLL_REGISTERED if pin was registered with dpll
- fix pin-id-get return and add extack errors
- fix device-id-get return and add extack errors
- drop pointless init of variables
- add macro for iterating over marked pins/devices
- move dpll_set_from_nlattr() for consistent order
- use GENL_REQ_ATTR_CHECK() for checking attibute presence
- fill extack if pin/device was not found
- drop pointless init of variables
- WARN_ON if dpll not registered on send event
- rename goto labels to indicate error path
- fix docs
- drop pointless init of variables
- verify pin in notify with a mark
- prevent ops->mode_set call if missing callback
- move static dpll_msg_add_pin_handle() from pin<->netdev patch
- split pin-parent nest:
  - pin-parent-device - for configuration of pin-device tuple
  - pin-parent-pin - for configuration od pin-pin tuple

 drivers/dpll/dpll_netlink.c | 1273 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   17 +
 include/linux/dpll.h        |    4 +
 3 files changed, 1294 insertions(+)
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..c44dda78737d
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,1273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include "dpll_core.h"
+#include "dpll_nl.h"
+#include <uapi/linux/dpll.h>
+
+#define ASSERT_NOT_NULL(ptr)	(WARN_ON(!ptr))
+
+#define xa_for_each_marked_start(xa, index, entry, filter, start) \
+	for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \
+	     entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))
+
+struct dpll_dump_ctx {
+	unsigned long idx;
+};
+
+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
+{
+	return (struct dpll_dump_ctx *)cb->ctx;
+}
+
+static int
+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+/**
+ * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
+ * @msg: pointer to sk_buff message to attach a pin handle
+ * @pin: pin pointer
+ *
+ * Return:
+ * * 0 - success
+ * * -EMSGSIZE - no space in message to attach pin handle
+ */
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	if (!pin)
+		return 0;
+	if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static int
+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	enum dpll_mode mode;
+	int ret;
+
+	ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_MODE, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_mode_supported(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 (!ops->mode_supported)
+		return 0;
+	for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
+		if (ops->mode_supported(dpll, dpll_priv(dpll), mode, extack))
+			if (nla_put_u8(msg, DPLL_A_MODE_SUPPORTED, mode))
+				return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
+			 struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	enum dpll_lock_status status;
+	int ret;
+
+	ret = ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
+		  struct netlink_ext_ack *extack)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	s32 temp;
+	int ret;
+
+	if (!ops->temp_get)
+		return 0;
+	ret = ops->temp_get(dpll, dpll_priv(dpll), &temp, extack);
+	if (ret)
+		return ret;
+	if (nla_put_s32(msg, DPLL_A_TEMP, temp))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(struct sk_buff *msg, struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref,
+		      struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	u32 prio;
+	int ret;
+
+	if (!ops->prio_get)
+		return 0;
+	ret = ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+			    dpll_priv(dpll), &prio, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
+			       struct dpll_pin_ref *ref,
+			       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	enum dpll_pin_state state;
+	int ret;
+
+
+	if (!ops->state_on_dpll_get)
+		return 0;
+	ret = ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				     dpll, dpll_priv(dpll), &state, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
+			   struct dpll_pin_ref *ref,
+			   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	enum dpll_pin_direction direction;
+	int ret;
+
+	ret = ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+				 dpll_priv(dpll), &direction, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
+		      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+	struct dpll_device *dpll = ref->dpll;
+	struct nlattr *nest;
+	int fs, ret;
+	u64 freq;
+
+	if (!ops->frequency_get)
+		return 0;
+	ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+				 dpll_priv(dpll), &freq, extack);
+	if (ret)
+		return ret;
+	if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
+		return -EMSGSIZE;
+	for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
+		nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
+		if (!nest)
+			return -EMSGSIZE;
+		freq = pin->prop->freq_supported[fs].min;
+		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
+				   &freq, 0)) {
+			nla_nest_cancel(msg, nest);
+			return -EMSGSIZE;
+		}
+		freq = pin->prop->freq_supported[fs].max;
+		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
+				   &freq, 0)) {
+			nla_nest_cancel(msg, nest);
+			return -EMSGSIZE;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+}
+
+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
+{
+	int fs;
+
+	for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
+		if (freq >= pin->prop->freq_supported[fs].min &&
+		    freq <= pin->prop->freq_supported[fs].max)
+			return true;
+	return false;
+}
+
+static int
+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
+			 struct dpll_pin_ref *dpll_ref,
+			 struct netlink_ext_ack *extack)
+{
+	enum dpll_pin_state state;
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *ppin;
+	struct nlattr *nest;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->parent_refs, index, ref) {
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+		void *parent_priv;
+
+		ppin = ref->pin;
+		parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
+		ret = ops->state_on_pin_get(pin,
+					    dpll_pin_on_pin_priv(ppin, pin),
+					    ppin, parent_priv, &state, extack);
+		if (ret)
+			return ret;
+		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
+		if (!nest)
+			return -EMSGSIZE;
+		if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		nla_nest_end(msg, nest);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_cancel(msg, nest);
+	return ret;
+}
+
+static int
+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
+		       struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *ref;
+	struct nlattr *attr;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->dpll_refs, index, ref) {
+		attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_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)
+			goto nest_cancel;
+		ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
+		if (ret)
+			goto nest_cancel;
+		ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
+		if (ret)
+			goto nest_cancel;
+		nla_nest_end(msg, attr);
+	}
+
+	return 0;
+
+nest_cancel:
+	nla_nest_end(msg, attr);
+	return ret;
+}
+
+static int
+dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
+			struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_properties *prop = pin->prop;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+	ASSERT_NOT_NULL(ref);
+	ret = dpll_msg_add_pin_handle(msg, pin);
+	if (ret)
+		return ret;
+	if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin->module)))
+		return -EMSGSIZE;
+	if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
+			  &pin->clock_id, 0))
+		return -EMSGSIZE;
+	if (prop->board_label &&
+	    nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
+		return -EMSGSIZE;
+	if (prop->panel_label &&
+	    nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
+		return -EMSGSIZE;
+	if (prop->package_label &&
+	    nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
+			   prop->package_label))
+		return -EMSGSIZE;
+	if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
+		return -EMSGSIZE;
+	if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop->capabilities))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
+	if (ret)
+		return ret;
+	if (xa_empty(&pin->parent_refs))
+		ret = dpll_msg_add_pin_dplls(msg, pin, extack);
+	else
+		ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
+
+	return ret;
+}
+
+static int
+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
+		    struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	ret = dpll_msg_add_dev_handle(msg, dpll);
+	if (ret)
+		return ret;
+	if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
+		return -EMSGSIZE;
+	if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		return -EMSGSIZE;
+	ret = dpll_msg_add_temp(msg, dpll, extack);
+	if (ret)
+		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;
+	ret = dpll_msg_add_mode_supported(msg, dpll, extack);
+	if (ret)
+		return ret;
+	if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
+		return -EMSGSIZE;
+
+	return ret;
+}
+
+static int
+dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int ret;
+
+	if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))
+		return -ENODEV;
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+	if (!hdr)
+		goto err_free_msg;
+	ret = dpll_device_get_one(dpll, msg, NULL);
+	if (ret)
+		goto err_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+err_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+err_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_device_create_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
+}
+
+int dpll_device_delete_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
+}
+
+int __dpll_device_change_ntf(struct dpll_device *dpll)
+{
+	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
+}
+
+/**
+ * dpll_device_change_ntf - notify that the dpll device has been changed
+ * @dpll: registered dpll pointer
+ *
+ * Context: acquires and holds a dpll_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_device_change_ntf(struct dpll_device *dpll)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = __dpll_device_change_ntf(dpll);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
+
+static int
+dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	int ret;
+
+	if (WARN_ON(!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)))
+		return -ENODEV;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+	if (!hdr)
+		goto err_free_msg;
+	ret = dpll_cmd_pin_get_one(msg, pin, NULL);
+	if (ret)
+		goto err_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+err_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+err_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_pin_create_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
+}
+
+int dpll_pin_delete_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
+}
+
+static int __dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+	return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
+}
+
+/**
+ * dpll_pin_change_ntf - notify that the pin has been changed
+ * @pin: registered pin pointer
+ *
+ * Context: acquires and holds a dpll_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = __dpll_pin_change_ntf(pin);
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
+
+static int
+dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
+		  struct netlink_ext_ack *extack)
+{
+	u64 freq = nla_get_u64(a);
+	struct dpll_pin_ref *ref;
+	unsigned long i;
+	int ret;
+
+	if (!dpll_pin_is_freq_supported(pin, freq)) {
+		NL_SET_ERR_MSG_FMT(extack, "not supported freq:%llu on pin:%u",
+				   freq, pin->id);
+		return -EINVAL;
+	}
+
+	xa_for_each(&pin->dpll_refs, i, ref) {
+		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+		struct dpll_device *dpll = ref->dpll;
+
+		if (!ops->frequency_set)
+			return -EOPNOTSUPP;
+		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+					 dpll, dpll_priv(dpll), freq, extack);
+		if (ret)
+			return ret;
+		__dpll_pin_change_ntf(pin);
+	}
+
+	return 0;
+}
+
+static int
+dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
+			  enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	struct dpll_pin_ref *parent_ref;
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *dpll_ref;
+	void *pin_priv, *parent_priv;
+	struct dpll_pin *parent;
+	unsigned long i;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change state",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	parent = xa_load(&dpll_pin_xa, parent_idx);
+	if (!parent)
+		return -EINVAL;
+	parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
+	if (!parent_ref)
+		return -EINVAL;
+	xa_for_each(&parent->dpll_refs, i, dpll_ref) {
+		ops = dpll_pin_ops(parent_ref);
+		if (!ops->state_on_pin_set)
+			return -EOPNOTSUPP;
+		pin_priv = dpll_pin_on_pin_priv(parent, pin);
+		parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, parent);
+		ret = ops->state_on_pin_set(pin, pin_priv, parent, parent_priv,
+					    state, extack);
+		if (ret)
+			return ret;
+	}
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		   enum dpll_pin_state state,
+		   struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change state",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->state_on_dpll_set)
+		return -EOPNOTSUPP;
+	ret = ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				     dpll, dpll_priv(dpll), state, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
+		  u32 prio, struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack, "pin:%u not allowed to change prio",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->prio_set)
+		return -EOPNOTSUPP;
+	ret = ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+			    dpll_priv(dpll), prio, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
+		       enum dpll_pin_direction direction,
+		       struct netlink_ext_ack *extack)
+{
+	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
+	int ret;
+
+	if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop->capabilities)) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "pin:%u not allowed to change direction",
+				   pin->id);
+		return -EOPNOTSUPP;
+	}
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	ops = dpll_pin_ops(ref);
+	if (!ops->direction_set)
+		return -EOPNOTSUPP;
+	ret = ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				 dpll, dpll_priv(dpll), direction, extack);
+	if (ret)
+		return ret;
+	__dpll_pin_change_ntf(pin);
+
+	return 0;
+}
+
+static int
+dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
+			   struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	enum dpll_pin_direction direction;
+	enum dpll_pin_state state;
+	struct dpll_pin_ref *ref;
+	struct dpll_device *dpll;
+	u32 pdpll_idx, prio;
+	int ret;
+
+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
+			 NULL, extack);
+	if (!tb[DPLL_A_ID]) {
+		NL_SET_ERR_MSG(extack, "device parent id expected");
+		return -EINVAL;
+	}
+	pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
+	dpll = xa_load(&dpll_device_xa, pdpll_idx);
+	if (!dpll)
+		return -EINVAL;
+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+	ASSERT_NOT_NULL(ref);
+	if (tb[DPLL_A_PIN_STATE]) {
+		state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+		ret = dpll_pin_state_set(dpll, pin, state, extack);
+		if (ret)
+			return ret;
+	}
+	if (tb[DPLL_A_PIN_PRIO]) {
+		prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
+		ret = dpll_pin_prio_set(dpll, pin, prio, extack);
+		if (ret)
+			return ret;
+	}
+	if (tb[DPLL_A_PIN_DIRECTION]) {
+		direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
+		ret = dpll_pin_direction_set(pin, dpll, direction, extack);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int
+dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
+			struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	enum dpll_pin_state state;
+	u32 ppin_idx;
+	int ret;
+
+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
+			 NULL, extack);
+	if (!tb[DPLL_A_PIN_ID]) {
+		NL_SET_ERR_MSG(extack, "parent pin id expected");
+		return -EINVAL;
+	}
+	ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
+	state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+	ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
+{
+	int rem, ret = -EINVAL;
+	struct nlattr *a;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLL_A_PIN_FREQUENCY:
+			ret = dpll_pin_freq_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_DEVICE:
+			ret = dpll_pin_parent_device_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_PARENT_PIN:
+			ret = dpll_pin_parent_pin_set(pin, a, info->extack);
+			if (ret)
+				return ret;
+			break;
+		case DPLL_A_PIN_ID:
+		case DPLL_A_ID:
+			break;
+		default:
+			NL_SET_ERR_MSG_FMT(info->extack,
+					   "unsupported attribute (%d)",
+					   nla_type(a));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static struct dpll_pin *
+dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
+	      enum dpll_pin_type type, struct nlattr *board_label,
+	      struct nlattr *panel_label, struct nlattr *package_label,
+	      struct netlink_ext_ack *extack)
+{
+	bool board_match, panel_match, package_match;
+	struct dpll_pin *pin_match = NULL, *pin;
+	const struct dpll_pin_properties *prop;
+	bool cid_match, mod_match, type_match;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {
+		prop = pin->prop;
+		cid_match = clock_id ? pin->clock_id == clock_id : true;
+		mod_match = mod_name_attr && module_name(pin->module) ?
+			!nla_strcmp(mod_name_attr,
+				    module_name(pin->module)) : true;
+		type_match = type ? prop->type == type : true;
+		board_match = board_label ? (prop->board_label ?
+			!nla_strcmp(board_label, prop->board_label) : false) :
+			true;
+		panel_match = panel_label ? (prop->panel_label ?
+			!nla_strcmp(panel_label, prop->panel_label) : false) :
+			true;
+		package_match = package_label ? (prop->package_label ?
+			!nla_strcmp(package_label, prop->package_label) :
+			false) : true;
+		if (cid_match && mod_match && type_match && board_match &&
+		    panel_match && package_match) {
+			if (pin_match) {
+				NL_SET_ERR_MSG(extack, "multiple matches");
+				return ERR_PTR(-EINVAL);
+			}
+			pin_match = pin;
+		};
+	}
+	if (!pin_match) {
+		NL_SET_ERR_MSG(extack, "not found");
+		return ERR_PTR(-ENODEV);
+	}
+	return pin_match;
+}
+
+static struct dpll_pin *dpll_pin_find_from_nlattr(struct genl_info *info)
+{
+	struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
+		*panel_label_attr = NULL, *package_label_attr = NULL;
+	enum dpll_pin_type type = 0;
+	u64 clock_id = 0;
+	int rem = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_CLOCK_ID:
+			if (clock_id)
+				goto duplicated_attr;
+			clock_id = nla_get_u64(attr);
+			break;
+		case DPLL_A_MODULE_NAME:
+			if (mod_name_attr)
+				goto duplicated_attr;
+			mod_name_attr = attr;
+			break;
+		case DPLL_A_PIN_TYPE:
+			if (type)
+				goto duplicated_attr;
+			type = nla_get_u8(attr);
+		break;
+		case DPLL_A_PIN_BOARD_LABEL:
+			if (board_label_attr)
+				goto duplicated_attr;
+			board_label_attr = attr;
+		break;
+		case DPLL_A_PIN_PANEL_LABEL:
+			if (panel_label_attr)
+				goto duplicated_attr;
+			panel_label_attr = attr;
+		break;
+		case DPLL_A_PIN_PACKAGE_LABEL:
+			if (package_label_attr)
+				goto duplicated_attr;
+			package_label_attr = attr;
+		break;
+		default:
+			break;
+		}
+	}
+	if (!(clock_id  || mod_name_attr || board_label_attr ||
+	      panel_label_attr || package_label_attr)) {
+		NL_SET_ERR_MSG(info->extack, "missing attributes");
+		return ERR_PTR(-EINVAL);
+	}
+	return dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
+			     panel_label_attr, package_label_attr,
+			     info->extack);
+duplicated_attr:
+	NL_SET_ERR_MSG(info->extack, "duplicated attribute");
+	return ERR_PTR(-EINVAL);
+}
+
+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin;
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_PIN_ID_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	pin = dpll_pin_find_from_nlattr(info);
+	if (!IS_ERR(pin)) {
+		ret = dpll_msg_add_pin_handle(msg, pin);
+		if (ret) {
+			nlmsg_free(msg);
+			return ret;
+		}
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[0];
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	if (!pin)
+		return -ENODEV;
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_PIN_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+	ret = dpll_cmd_pin_get_one(msg, pin, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_pin *pin;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret = 0;
+
+	xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,
+				 ctx->idx) {
+		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+				  cb->nlh->nlmsg_seq,
+				  &dpll_nl_family, NLM_F_MULTI,
+				  DPLL_CMD_PIN_GET);
+		if (!hdr) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);
+		if (ret) {
+			genlmsg_cancel(skb, hdr);
+			break;
+		}
+		genlmsg_end(skb, hdr);
+	}
+	if (ret == -EMSGSIZE) {
+		ctx->idx = i;
+		return skb->len;
+	}
+	return ret;
+}
+
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_pin *pin = info->user_ptr[0];
+
+	return dpll_pin_set_from_nlattr(pin, info);
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+	struct nlattr *tb[DPLL_A_MAX + 1];
+	int ret;
+
+	nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
+		  genlmsg_len(info->genlhdr), NULL, info->extack);
+	if (tb[DPLL_A_MODE]) {
+		if (!ops->mode_set) {
+			NL_SET_ERR_MSG(info->extack, "mode set not supported");
+			return -EINVAL;
+		}
+		ret = ops->mode_set(dpll, dpll_priv(dpll),
+				    nla_get_u8(tb[DPLL_A_MODE]), info->extack);
+		if (ret)
+			return ret;
+		__dpll_device_change_ntf(dpll);
+	}
+
+	return 0;
+}
+
+static struct dpll_device *
+dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
+		 enum dpll_type type, struct netlink_ext_ack *extack)
+{
+	struct dpll_device *dpll_match = NULL, *dpll;
+	bool cid_match, mod_match, type_match;
+	unsigned long i;
+
+	xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+		cid_match = clock_id ? dpll->clock_id == clock_id : true;
+		mod_match = mod_name_attr ? (module_name(dpll->module) ?
+			!nla_strcmp(mod_name_attr,
+				    module_name(dpll->module)) : false) : true;
+		type_match = type ? dpll->type == type : true;
+		if (cid_match && mod_match && type_match) {
+			if (dpll_match) {
+				NL_SET_ERR_MSG(extack, "multiple matches");
+				return ERR_PTR(-EINVAL);
+			}
+			dpll_match = dpll;
+		}
+	}
+	if (!dpll_match) {
+		NL_SET_ERR_MSG(extack, "not found");
+		return ERR_PTR(-ENODEV);
+	}
+
+	return dpll_match;
+}
+
+static struct dpll_device *
+dpll_device_find_from_nlattr(struct genl_info *info)
+{
+	struct nlattr *attr, *mod_name_attr = NULL;
+	enum dpll_type type = 0;
+	u64 clock_id = 0;
+	int rem = 0;
+
+	nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(attr)) {
+		case DPLL_A_CLOCK_ID:
+			if (clock_id)
+				goto duplicated_attr;
+			clock_id = nla_get_u64(attr);
+			break;
+		case DPLL_A_MODULE_NAME:
+			if (mod_name_attr)
+				goto duplicated_attr;
+			mod_name_attr = attr;
+			break;
+		case DPLL_A_TYPE:
+			if (type)
+				goto duplicated_attr;
+			type = nla_get_u8(attr);
+			break;
+		default:
+			break;
+		}
+	}
+	if (!clock_id && !mod_name_attr && !type) {
+		NL_SET_ERR_MSG(info->extack, "missing attributes");
+		return ERR_PTR(-EINVAL);
+	}
+	return dpll_device_find(clock_id, mod_name_attr, type, info->extack);
+duplicated_attr:
+	NL_SET_ERR_MSG(info->extack, "duplicated attribute");
+	return ERR_PTR(-EINVAL);
+}
+
+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll;
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_DEVICE_ID_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	dpll = dpll_device_find_from_nlattr(info);
+	if (!IS_ERR(dpll)) {
+		ret = dpll_msg_add_dev_handle(msg, dpll);
+		if (ret) {
+			nlmsg_free(msg);
+			return ret;
+		}
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct sk_buff *msg;
+	struct nlattr *hdr;
+	int ret;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+				DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	ret = dpll_device_get_one(dpll, msg, info->extack);
+	if (ret) {
+		nlmsg_free(msg);
+		return ret;
+	}
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+
+	return dpll_set_from_nlattr(dpll, info);
+}
+
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_device *dpll;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret = 0;
+
+	xa_for_each_marked_start(&dpll_device_xa, i, dpll, DPLL_REGISTERED,
+				 ctx->idx) {
+		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+				  cb->nlh->nlmsg_seq, &dpll_nl_family,
+				  NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
+		if (!hdr) {
+			ret = -EMSGSIZE;
+			break;
+		}
+		ret = dpll_device_get_one(dpll, skb, cb->extack);
+		if (ret) {
+			genlmsg_cancel(skb, hdr);
+			break;
+		}
+		genlmsg_end(skb, hdr);
+	}
+	if (ret == -EMSGSIZE) {
+		ctx->idx = i;
+		return skb->len;
+	}
+	return ret;
+}
+
+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		  struct genl_info *info)
+{
+	u32 id;
+
+	if (GENL_REQ_ATTR_CHECK(info, DPLL_A_ID))
+		return -EINVAL;
+
+	mutex_lock(&dpll_lock);
+	id = nla_get_u32(info->attrs[DPLL_A_ID]);
+	info->user_ptr[0] = dpll_device_get_by_id(id);
+	if (!info->user_ptr[0]) {
+		NL_SET_ERR_MSG(info->extack, "device not found");
+		goto unlock;
+	}
+	return 0;
+unlock:
+	mutex_unlock(&dpll_lock);
+	return -ENODEV;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		    struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+int
+dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		     struct genl_info *info)
+{
+	mutex_lock(&dpll_lock);
+
+	return 0;
+}
+
+void
+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		   struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+int dpll_lock_dumpit(struct netlink_callback *cb)
+{
+	mutex_lock(&dpll_lock);
+
+	return 0;
+}
+
+int dpll_unlock_dumpit(struct netlink_callback *cb)
+{
+	mutex_unlock(&dpll_lock);
+
+	return 0;
+}
+
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		      struct genl_info *info)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	if (GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID)) {
+		ret = -EINVAL;
+		goto unlock_dev;
+	}
+	info->user_ptr[0] = xa_load(&dpll_pin_xa,
+				    nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
+	if (!info->user_ptr[0]) {
+		NL_SET_ERR_MSG(info->extack, "pin not found");
+		ret = -ENODEV;
+		goto unlock_dev;
+	}
+
+	return 0;
+
+unlock_dev:
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	mutex_unlock(&dpll_lock);
+}
+
+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..2e3a6b5984e4
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ *  Copyright (c) 2023 Intel and affiliates
+ */
+
+int dpll_device_create_ntf(struct dpll_device *dpll);
+
+int dpll_device_delete_ntf(struct dpll_device *dpll);
+
+int dpll_pin_create_ntf(struct dpll_pin *pin);
+
+int dpll_pin_delete_ntf(struct dpll_pin *pin);
+
+int __init dpll_netlink_init(void);
+
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index d3258205e499..a96fddbc5279 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -133,4 +133,8 @@ int dpll_pin_on_pin_register(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);
 
+int dpll_device_change_ntf(struct dpll_device *dpll);
+
+int dpll_pin_change_ntf(struct dpll_pin *pin);
+
 #endif
-- 
2.27.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] 109+ messages in thread

* [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:18   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Jiri Pirko, Milena Olech, Michal Michalik, Vadim Fedorenko,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

From: Jiri Pirko <jiri@nvidia.com>

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

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

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

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- rearrange function definition according to usage
v8->v9:
- net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
  code in net/core/rtnetlink.c to respect that.
- move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
  function with this patch

 drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
 include/linux/dpll.h         | 20 ++++++++++++++++++++
 include/linux/netdevice.h    | 20 ++++++++++++++++++++
 include/uapi/linux/if_link.h |  2 ++
 net/core/dev.c               | 22 ++++++++++++++++++++++
 net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index c44dda78737d..e4a9bd767b92 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
 	return 0;
 }
 
+/**
+ * dpll_msg_pin_handle_size - get size of pin handle attribute for given pin
+ * @pin: pin pointer
+ *
+ * Return: byte size of pin handle attribute for given pin.
+ */
+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+	return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
+}
+EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
+
 /**
  * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
  * @msg: pointer to sk_buff message to attach a pin handle
@@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
 		return -EMSGSIZE;
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
 
 static int
 dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
@@ -267,10 +280,9 @@ dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
 		if (!nest)
 			return -EMSGSIZE;
-		if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
-			ret = -EMSGSIZE;
+		ret = dpll_msg_add_pin_handle(msg, ppin);
+		if (ret)
 			goto nest_cancel;
-		}
 		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
 			ret = -EMSGSIZE;
 			goto nest_cancel;
@@ -330,6 +342,7 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
 
 	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
 	ASSERT_NOT_NULL(ref);
+
 	ret = dpll_msg_add_pin_handle(msg, pin);
 	if (ret)
 		return ret;
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index a96fddbc5279..16470f508487 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -104,6 +104,26 @@ struct dpll_pin_properties {
 	struct dpll_pin_frequency *freq_supported;
 };
 
+#if IS_ENABLED(CONFIG_DPLL)
+
+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin);
+
+#else
+
+static inline size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+	return 0;
+}
+
+static inline int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	return 0;
+}
+
+#endif
+
 struct dpll_device
 *dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b828c7a75be2..a4865c131e40 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
@@ -2058,6 +2059,9 @@ enum netdev_ml_priv_type {
  *			SET_NETDEV_DEVLINK_PORT macro. This pointer is static
  *			during the time netdevice is registered.
  *
+ *	@dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
+ *		   where the clock is recovered.
+ *
  *	FIXME: cleanup struct net_device such that network protocol info
  *	moves out.
  */
@@ -2414,6 +2418,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)
 
@@ -3961,6 +3969,18 @@ 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);
+
+static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+	return dev->dpll_pin;
+#else
+	return NULL;
+#endif
+}
+
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
 				    struct netdev_queue *txq, int *ret);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 0f6a0fe09bdb..be03c8292cd7 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -377,6 +377,8 @@ enum {
 	IFLA_GSO_IPV4_MAX_SIZE,
 	IFLA_GRO_IPV4_MAX_SIZE,
 
+	IFLA_DPLL_PIN,
+
 	__IFLA_MAX
 };
 
diff --git a/net/core/dev.c b/net/core/dev.c
index d6e1b786c5c5..ad4f12fed4a7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8952,6 +8952,28 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b)
 }
 EXPORT_SYMBOL(netdev_port_same_parent_id);
 
+static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+	rtnl_lock();
+	dev->dpll_pin = dpll_pin;
+	rtnl_unlock();
+#endif
+}
+
+void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+	WARN_ON(!dpll_pin);
+	netdev_dpll_pin_assign(dev, dpll_pin);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_set);
+
+void netdev_dpll_pin_clear(struct net_device *dev)
+{
+	netdev_dpll_pin_assign(dev, NULL);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_clear);
+
 /**
  *	dev_change_proto_down - set carrier according to proto_down.
  *
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 70838d7e5b32..7d651dcb49fd 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1055,6 +1055,15 @@ 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 */
+
+	size += dpll_msg_pin_handle_size(netdev_dpll_pin(dev));
+
+	return size;
+}
+
 static noinline size_t if_nlmsg_size(const struct net_device *dev,
 				     u32 ext_filter_mask)
 {
@@ -1111,6 +1120,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;
 }
 
@@ -1774,6 +1784,28 @@ 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;
+
+	ret = dpll_msg_add_pin_handle(skb, netdev_dpll_pin(dev));
+	if (ret < 0)
+		goto nest_cancel;
+
+	nla_nest_end(skb, dpll_pin_nest);
+	return 0;
+
+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,
@@ -1916,6 +1948,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.27.0


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

* [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
@ 2023-07-20  9:18   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:18 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Jiri Pirko, Milena Olech, Michal Michalik, Vadim Fedorenko,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

From: Jiri Pirko <jiri@nvidia.com>

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

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

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

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- rearrange function definition according to usage
v8->v9:
- net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
  code in net/core/rtnetlink.c to respect that.
- move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
  function with this patch

 drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
 include/linux/dpll.h         | 20 ++++++++++++++++++++
 include/linux/netdevice.h    | 20 ++++++++++++++++++++
 include/uapi/linux/if_link.h |  2 ++
 net/core/dev.c               | 22 ++++++++++++++++++++++
 net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index c44dda78737d..e4a9bd767b92 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
 	return 0;
 }
 
+/**
+ * dpll_msg_pin_handle_size - get size of pin handle attribute for given pin
+ * @pin: pin pointer
+ *
+ * Return: byte size of pin handle attribute for given pin.
+ */
+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+	return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
+}
+EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
+
 /**
  * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
  * @msg: pointer to sk_buff message to attach a pin handle
@@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
 		return -EMSGSIZE;
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
 
 static int
 dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
@@ -267,10 +280,9 @@ dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
 		nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
 		if (!nest)
 			return -EMSGSIZE;
-		if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
-			ret = -EMSGSIZE;
+		ret = dpll_msg_add_pin_handle(msg, ppin);
+		if (ret)
 			goto nest_cancel;
-		}
 		if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
 			ret = -EMSGSIZE;
 			goto nest_cancel;
@@ -330,6 +342,7 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
 
 	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
 	ASSERT_NOT_NULL(ref);
+
 	ret = dpll_msg_add_pin_handle(msg, pin);
 	if (ret)
 		return ret;
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index a96fddbc5279..16470f508487 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -104,6 +104,26 @@ struct dpll_pin_properties {
 	struct dpll_pin_frequency *freq_supported;
 };
 
+#if IS_ENABLED(CONFIG_DPLL)
+
+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin);
+
+#else
+
+static inline size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+	return 0;
+}
+
+static inline int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+	return 0;
+}
+
+#endif
+
 struct dpll_device
 *dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b828c7a75be2..a4865c131e40 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
@@ -2058,6 +2059,9 @@ enum netdev_ml_priv_type {
  *			SET_NETDEV_DEVLINK_PORT macro. This pointer is static
  *			during the time netdevice is registered.
  *
+ *	@dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
+ *		   where the clock is recovered.
+ *
  *	FIXME: cleanup struct net_device such that network protocol info
  *	moves out.
  */
@@ -2414,6 +2418,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)
 
@@ -3961,6 +3969,18 @@ 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);
+
+static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+	return dev->dpll_pin;
+#else
+	return NULL;
+#endif
+}
+
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
 				    struct netdev_queue *txq, int *ret);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 0f6a0fe09bdb..be03c8292cd7 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -377,6 +377,8 @@ enum {
 	IFLA_GSO_IPV4_MAX_SIZE,
 	IFLA_GRO_IPV4_MAX_SIZE,
 
+	IFLA_DPLL_PIN,
+
 	__IFLA_MAX
 };
 
diff --git a/net/core/dev.c b/net/core/dev.c
index d6e1b786c5c5..ad4f12fed4a7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8952,6 +8952,28 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b)
 }
 EXPORT_SYMBOL(netdev_port_same_parent_id);
 
+static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+	rtnl_lock();
+	dev->dpll_pin = dpll_pin;
+	rtnl_unlock();
+#endif
+}
+
+void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+	WARN_ON(!dpll_pin);
+	netdev_dpll_pin_assign(dev, dpll_pin);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_set);
+
+void netdev_dpll_pin_clear(struct net_device *dev)
+{
+	netdev_dpll_pin_assign(dev, NULL);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_clear);
+
 /**
  *	dev_change_proto_down - set carrier according to proto_down.
  *
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 70838d7e5b32..7d651dcb49fd 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1055,6 +1055,15 @@ 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 */
+
+	size += dpll_msg_pin_handle_size(netdev_dpll_pin(dev));
+
+	return size;
+}
+
 static noinline size_t if_nlmsg_size(const struct net_device *dev,
 				     u32 ext_filter_mask)
 {
@@ -1111,6 +1120,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;
 }
 
@@ -1774,6 +1784,28 @@ 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;
+
+	ret = dpll_msg_add_pin_handle(skb, netdev_dpll_pin(dev));
+	if (ret < 0)
+		goto nest_cancel;
+
+	nla_nest_end(skb, dpll_pin_nest);
+	return 0;
+
+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,
@@ -1916,6 +1948,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.27.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] 109+ messages in thread

* [PATCH net-next 08/11] ice: add admin commands to access cgu configuration
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:19   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- add support for DPLL_MODE_FREERUN mode
- remove freq from ice_aq_get_phy_rec_clk_out() as no longer supported
- add node_handle out argument to ice_aq_get_phy_rec_clk_out()

v8->v9:
- rename MAX_NETLIST_SIZE -> ICE_MAX_NETLIST_SIZE
- simplify function: s64 convert_s48_to_s64(s64 signed_48)
- do not assign 0 to field that is already 0

 drivers/net/ethernet/intel/ice/ice.h          |   2 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   | 245 ++++++++-
 drivers/net/ethernet/intel/ice/ice_common.c   | 467 ++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_common.h   |  44 ++
 drivers/net/ethernet/intel/ice/ice_lib.c      |  17 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   | 419 ++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 231 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1421 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 4ba3d99439a0..484d1d143174 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -198,7 +198,9 @@
 enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
+	ICE_F_PHY_RCLK,
 	ICE_F_SMA_CTRL,
+	ICE_F_CGU,
 	ICE_F_GNSS,
 	ICE_F_MAX
 };
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 63d3e1dcbba5..4d36abfa07c0 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1339,6 +1339,30 @@ 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[11];
+	__le16 node_handle;
+};
+
 struct ice_aqc_link_topo_params {
 	u8 lport_num;
 	u8 lport_num_valid;
@@ -1355,6 +1379,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 +1417,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 +2110,193 @@ 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)
+	u8 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)
+	u8 config;
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL		ICE_M(0x1F, 0)
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_SHIFT		5
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE		\
+	ICE_M(0x7, ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_SHIFT)
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_FREERUN	0
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_AUTOMATIC	\
+	ICE_M(0x3, ICE_AQC_GET_CGU_DPLL_CONFIG_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_SHIFT		5
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE		\
+	ICE_M(0x7, ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_SHIFT)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN	0
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC	\
+	ICE_M(0x3, ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_SHIFT)
+	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 +2366,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 +2407,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 +2539,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 +2595,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 e16d4c83ed5f..297aa76d2bdb 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -7,6 +7,7 @@
 #include "ice_flow.h"
 
 #define ICE_PF_RESET_WAIT_COUNT	300
+#define ICE_MAX_NETLIST_SIZE	10
 
 static const char * const ice_link_mode_str_low[] = {
 	[0] = "100BASE_TX",
@@ -434,6 +435,81 @@ 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;
+}
+
+/**
+ * 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 < ICE_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
@@ -4915,6 +4991,397 @@ 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 s64 convert_s48_to_s64(s64 signed_48)
+{
+	return signed_48 & BIT_ULL(47) ?
+		GENMASK_ULL(63, 48) | 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,
+			   u8 *dpll_state, u8 *config, 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 = cmd->dpll_state;
+		*config = cmd->config;
+		*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
+ * @node_handle: 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, u16 *node_handle)
+{
+	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;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*phy_output = cmd->phy_output;
+		if (port_num)
+			*port_num = cmd->port_num;
+		if (flags)
+			*flags = cmd->flags;
+		if (node_handle)
+			*node_handle = le16_to_cpu(cmd->node_handle);
+	}
+
+	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 81961a7d6598..fd217112d6d8 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,44 @@ 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,
+			   u8 *dpll_state, u8 *config, 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, u16 *node_handle);
 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 00e3afd507a4..0adc860bb716 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4030,13 +4030,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..e4e66ea48631 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3213,6 +3213,90 @@ 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;
+
+	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 +3465,338 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
 		return ice_get_phy_tx_tstamp_ready_e822(hw, block,
 							tstamp_ready);
 }
+
+/**
+ * ice_is_cgu_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Generation Unit (CGU) device is present in the netlist
+ * Return:
+ * * true - cgu is present
+ * * false - cgu is not present
+ */
+bool ice_is_cgu_present(struct ice_hw *hw)
+{
+	if (!ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				   ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
+				   NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032;
+		return true;
+	} else if (!ice_find_netlist_node(hw,
+					  ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+					  ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384,
+					  NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * ice_cgu_get_pin_desc_e823
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pin
+ * @size: number of inputs/outputs
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc_e823(struct ice_hw *hw, bool input, int *size)
+{
+	static const struct ice_cgu_pin_desc *t;
+
+	if (hw->cgu_part_number ==
+	    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032) {
+		if (input) {
+			t = ice_e823_zl_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_inputs);
+		} else {
+			t = ice_e823_zl_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_outputs);
+		}
+	} else if (hw->cgu_part_number ==
+		   ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384) {
+		if (input) {
+			t = ice_e823_si_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_inputs);
+		} else {
+			t = ice_e823_si_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_outputs);
+		}
+	} else {
+		t = NULL;
+		*size = 0;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_desc
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pins
+ * @size: size of array returned by function
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size)
+{
+	const struct ice_cgu_pin_desc *t = NULL;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+		if (input) {
+			t = ice_e810t_sfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_inputs);
+		} else {
+			t = ice_e810t_sfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E810C_QSFP:
+		if (input) {
+			t = ice_e810t_qsfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_inputs);
+		} else {
+			t = ice_e810t_qsfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		t = ice_cgu_get_pin_desc_e823(hw, input, size);
+		break;
+	default:
+		break;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_type
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: type of a pin.
+ */
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].type;
+}
+
+/**
+ * ice_cgu_get_pin_freq_supp
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ * @num: output number of supported frequencies
+ *
+ * Get frequency supported number and array of supported frequencies.
+ *
+ * Return: array of supported frequencies for given pin.
+ */
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	*num = 0;
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+	if (!t)
+		return NULL;
+	if (pin >= t_size)
+		return NULL;
+	*num = t[pin].freq_supp_num;
+
+	return t[pin].freq_supp;
+}
+
+/**
+ * ice_cgu_get_pin_name
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return:
+ * * null terminated char array with name
+ * * NULL in case of failure
+ */
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return NULL;
+
+	if (pin >= t_size)
+		return NULL;
+
+	return t[pin].name;
+}
+
+/**
+ * ice_get_cgu_state - get the state of the DPLL
+ * @hw: pointer to the hw struct
+ * @dpll_idx: Index of internal DPLL unit
+ * @last_dpll_state: last known state of DPLL
+ * @pin: pointer to a buffer for returning currently active pin
+ * @ref_state: reference clock state
+ * @phase_offset: pointer to a buffer for returning phase offset
+ * @dpll_state: state of the DPLL (output)
+ *
+ * This function will read the state of the DPLL(dpll_idx). Non-null
+ * 'pin', 'ref_state', 'eec_mode' and 'phase_offset' parameters are used to
+ * retrieve currently active pin, state, mode and phase_offset respectively.
+ *
+ * Return: state of the DPLL
+ */
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum dpll_lock_status last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum dpll_lock_status *dpll_state,
+		      enum dpll_mode *mode)
+{
+	u8 hw_ref_state, hw_dpll_state, hw_eec_mode, hw_config;
+	s64 hw_phase_offset;
+	int status;
+
+	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
+					    &hw_dpll_state, &hw_config,
+					    &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_config & ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL;
+	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;
+	if (mode) {
+		switch (hw_config & ICE_AQC_GET_CGU_DPLL_CONFIG_MODE) {
+		case ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_FREERUN:
+			*mode = DPLL_MODE_FREERUN;
+			*dpll_state = DPLL_LOCK_STATUS_UNLOCKED;
+			return status;
+		case ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_AUTOMATIC:
+			*mode = DPLL_MODE_AUTOMATIC;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* 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 = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
+		else
+			*dpll_state = DPLL_LOCK_STATUS_LOCKED;
+	} else if (last_dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ ||
+		   last_dpll_state == DPLL_LOCK_STATUS_HOLDOVER) {
+		*dpll_state = DPLL_LOCK_STATUS_HOLDOVER;
+	} else {
+		*dpll_state = DPLL_LOCK_STATUS_UNLOCKED;
+	}
+
+	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..b585bbadda91 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -3,6 +3,7 @@
 
 #ifndef _ICE_PTP_HW_H_
 #define _ICE_PTP_HW_H_
+#include <linux/dpll.h>
 
 enum ice_ptp_tmr_cmd {
 	INIT_TIME,
@@ -109,6 +110,222 @@ struct ice_cgu_pll_params_e822 {
 	u32 post_pll_div;
 };
 
+#define E810C_QSFP_C827_0_HANDLE	2
+#define E810C_QSFP_C827_1_HANDLE	3
+enum ice_e810_c827_idx {
+	C827_0,
+	C827_1
+};
+
+enum ice_phy_rclk_pins {
+	ICE_RCLKA_PIN = 0,		/* SCL pin */
+	ICE_RCLKB_PIN,			/* SDA pin */
+};
+
+#define ICE_E810_RCLK_PINS_NUM		(ICE_RCLKB_PIN + 1)
+#define ICE_E822_RCLK_PINS_NUM		(ICE_RCLKA_PIN + 1)
+#define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
+					  (_pin) + ZL_REF1P)
+enum ice_cgu_state {
+	ICE_CGU_STATE_UNKNOWN = -1,
+	ICE_CGU_STATE_INVALID,		/* state is not valid */
+	ICE_CGU_STATE_FREERUN,		/* clock is free-running */
+	ICE_CGU_STATE_LOCKED,		/* clock is locked to the reference,
+					 * but the holdover memory is not valid
+					 */
+	ICE_CGU_STATE_LOCKED_HO_ACQ,	/* clock is locked to the reference
+					 * and holdover memory is valid
+					 */
+	ICE_CGU_STATE_HOLDOVER,		/* clock is in holdover mode */
+	ICE_CGU_STATE_MAX
+};
+
+#define MAX_CGU_STATE_NAME_LEN		14
+struct ice_cgu_state_desc {
+	char name[MAX_CGU_STATE_NAME_LEN];
+	enum ice_cgu_state state;
+};
+
+enum ice_zl_cgu_in_pins {
+	ZL_REF0P = 0,
+	ZL_REF0N,
+	ZL_REF1P,
+	ZL_REF1N,
+	ZL_REF2P,
+	ZL_REF2N,
+	ZL_REF3P,
+	ZL_REF3N,
+	ZL_REF4P,
+	ZL_REF4N,
+	NUM_ZL_CGU_INPUT_PINS
+};
+
+enum ice_zl_cgu_out_pins {
+	ZL_OUT0 = 0,
+	ZL_OUT1,
+	ZL_OUT2,
+	ZL_OUT3,
+	ZL_OUT4,
+	ZL_OUT5,
+	ZL_OUT6,
+	NUM_ZL_CGU_OUTPUT_PINS
+};
+
+enum ice_si_cgu_in_pins {
+	SI_REF0P = 0,
+	SI_REF0N,
+	SI_REF1P,
+	SI_REF1N,
+	SI_REF2P,
+	SI_REF2N,
+	SI_REF3,
+	SI_REF4,
+	NUM_SI_CGU_INPUT_PINS
+};
+
+enum ice_si_cgu_out_pins {
+	SI_OUT0 = 0,
+	SI_OUT1,
+	SI_OUT2,
+	SI_OUT3,
+	SI_OUT4,
+	NUM_SI_CGU_OUTPUT_PINS
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_common[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+	DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_1_hz[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_10_mhz[] = {
+	DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+struct ice_cgu_pin_desc {
+	char *name;
+	u8 index;
+	enum dpll_pin_type type;
+	u32 freq_supp_num;
+	struct dpll_pin_frequency *freq_supp;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, 0, 0 },
+	{ "NONE",	  SI_REF0N, 0, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	  SI_REF2N, 0, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, 0, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+	{ "NONE",	  ZL_REF2P, 0, 0 },
+	{ "NONE",	  ZL_REF2N, 0, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	  ZL_REF3N, 0, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	   ZL_OUT5, 0, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +414,20 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data);
 int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data);
 int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data);
 bool ice_is_pca9575_present(struct ice_hw *hw);
+bool ice_is_phy_rclk_present(struct ice_hw *hw);
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw);
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx);
+bool ice_is_cgu_present(struct ice_hw *hw);
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input);
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num);
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input);
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum dpll_lock_status last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum dpll_lock_status *dpll_state,
+		      enum dpll_mode *mode);
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);
 
 #define PFTSYN_SEM_BYTES	4
 
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index a09556e57803..128bc4d326f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -963,6 +963,7 @@ struct ice_hw {
 	DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX);
 	u8 dvm_ena;
 	u16 io_expander_handle;
+	u8 cgu_part_number;
 };
 
 /* Statistics collected by each port, VSI, VEB, and S-channel */
-- 
2.27.0


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

* [PATCH net-next 08/11] ice: add admin commands to access cgu configuration
@ 2023-07-20  9:19   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- add support for DPLL_MODE_FREERUN mode
- remove freq from ice_aq_get_phy_rec_clk_out() as no longer supported
- add node_handle out argument to ice_aq_get_phy_rec_clk_out()

v8->v9:
- rename MAX_NETLIST_SIZE -> ICE_MAX_NETLIST_SIZE
- simplify function: s64 convert_s48_to_s64(s64 signed_48)
- do not assign 0 to field that is already 0

 drivers/net/ethernet/intel/ice/ice.h          |   2 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   | 245 ++++++++-
 drivers/net/ethernet/intel/ice/ice_common.c   | 467 ++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_common.h   |  44 ++
 drivers/net/ethernet/intel/ice/ice_lib.c      |  17 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   | 419 ++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 231 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1421 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 4ba3d99439a0..484d1d143174 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -198,7 +198,9 @@
 enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
+	ICE_F_PHY_RCLK,
 	ICE_F_SMA_CTRL,
+	ICE_F_CGU,
 	ICE_F_GNSS,
 	ICE_F_MAX
 };
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 63d3e1dcbba5..4d36abfa07c0 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1339,6 +1339,30 @@ 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[11];
+	__le16 node_handle;
+};
+
 struct ice_aqc_link_topo_params {
 	u8 lport_num;
 	u8 lport_num_valid;
@@ -1355,6 +1379,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 +1417,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 +2110,193 @@ 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)
+	u8 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)
+	u8 config;
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL		ICE_M(0x1F, 0)
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_SHIFT		5
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE		\
+	ICE_M(0x7, ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_SHIFT)
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_FREERUN	0
+#define ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_AUTOMATIC	\
+	ICE_M(0x3, ICE_AQC_GET_CGU_DPLL_CONFIG_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_SHIFT		5
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE		\
+	ICE_M(0x7, ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_SHIFT)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN	0
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC	\
+	ICE_M(0x3, ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_SHIFT)
+	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 +2366,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 +2407,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 +2539,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 +2595,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 e16d4c83ed5f..297aa76d2bdb 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -7,6 +7,7 @@
 #include "ice_flow.h"
 
 #define ICE_PF_RESET_WAIT_COUNT	300
+#define ICE_MAX_NETLIST_SIZE	10
 
 static const char * const ice_link_mode_str_low[] = {
 	[0] = "100BASE_TX",
@@ -434,6 +435,81 @@ 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;
+}
+
+/**
+ * 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 < ICE_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
@@ -4915,6 +4991,397 @@ 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 s64 convert_s48_to_s64(s64 signed_48)
+{
+	return signed_48 & BIT_ULL(47) ?
+		GENMASK_ULL(63, 48) | 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,
+			   u8 *dpll_state, u8 *config, 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 = cmd->dpll_state;
+		*config = cmd->config;
+		*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
+ * @node_handle: 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, u16 *node_handle)
+{
+	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;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*phy_output = cmd->phy_output;
+		if (port_num)
+			*port_num = cmd->port_num;
+		if (flags)
+			*flags = cmd->flags;
+		if (node_handle)
+			*node_handle = le16_to_cpu(cmd->node_handle);
+	}
+
+	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 81961a7d6598..fd217112d6d8 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,44 @@ 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,
+			   u8 *dpll_state, u8 *config, 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, u16 *node_handle);
 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 00e3afd507a4..0adc860bb716 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4030,13 +4030,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..e4e66ea48631 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3213,6 +3213,90 @@ 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;
+
+	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 +3465,338 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
 		return ice_get_phy_tx_tstamp_ready_e822(hw, block,
 							tstamp_ready);
 }
+
+/**
+ * ice_is_cgu_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Generation Unit (CGU) device is present in the netlist
+ * Return:
+ * * true - cgu is present
+ * * false - cgu is not present
+ */
+bool ice_is_cgu_present(struct ice_hw *hw)
+{
+	if (!ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				   ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
+				   NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032;
+		return true;
+	} else if (!ice_find_netlist_node(hw,
+					  ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+					  ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384,
+					  NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * ice_cgu_get_pin_desc_e823
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pin
+ * @size: number of inputs/outputs
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc_e823(struct ice_hw *hw, bool input, int *size)
+{
+	static const struct ice_cgu_pin_desc *t;
+
+	if (hw->cgu_part_number ==
+	    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032) {
+		if (input) {
+			t = ice_e823_zl_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_inputs);
+		} else {
+			t = ice_e823_zl_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_outputs);
+		}
+	} else if (hw->cgu_part_number ==
+		   ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384) {
+		if (input) {
+			t = ice_e823_si_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_inputs);
+		} else {
+			t = ice_e823_si_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_outputs);
+		}
+	} else {
+		t = NULL;
+		*size = 0;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_desc
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pins
+ * @size: size of array returned by function
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size)
+{
+	const struct ice_cgu_pin_desc *t = NULL;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+		if (input) {
+			t = ice_e810t_sfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_inputs);
+		} else {
+			t = ice_e810t_sfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E810C_QSFP:
+		if (input) {
+			t = ice_e810t_qsfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_inputs);
+		} else {
+			t = ice_e810t_qsfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		t = ice_cgu_get_pin_desc_e823(hw, input, size);
+		break;
+	default:
+		break;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_type
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: type of a pin.
+ */
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].type;
+}
+
+/**
+ * ice_cgu_get_pin_freq_supp
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ * @num: output number of supported frequencies
+ *
+ * Get frequency supported number and array of supported frequencies.
+ *
+ * Return: array of supported frequencies for given pin.
+ */
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	*num = 0;
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+	if (!t)
+		return NULL;
+	if (pin >= t_size)
+		return NULL;
+	*num = t[pin].freq_supp_num;
+
+	return t[pin].freq_supp;
+}
+
+/**
+ * ice_cgu_get_pin_name
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return:
+ * * null terminated char array with name
+ * * NULL in case of failure
+ */
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return NULL;
+
+	if (pin >= t_size)
+		return NULL;
+
+	return t[pin].name;
+}
+
+/**
+ * ice_get_cgu_state - get the state of the DPLL
+ * @hw: pointer to the hw struct
+ * @dpll_idx: Index of internal DPLL unit
+ * @last_dpll_state: last known state of DPLL
+ * @pin: pointer to a buffer for returning currently active pin
+ * @ref_state: reference clock state
+ * @phase_offset: pointer to a buffer for returning phase offset
+ * @dpll_state: state of the DPLL (output)
+ *
+ * This function will read the state of the DPLL(dpll_idx). Non-null
+ * 'pin', 'ref_state', 'eec_mode' and 'phase_offset' parameters are used to
+ * retrieve currently active pin, state, mode and phase_offset respectively.
+ *
+ * Return: state of the DPLL
+ */
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum dpll_lock_status last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum dpll_lock_status *dpll_state,
+		      enum dpll_mode *mode)
+{
+	u8 hw_ref_state, hw_dpll_state, hw_eec_mode, hw_config;
+	s64 hw_phase_offset;
+	int status;
+
+	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
+					    &hw_dpll_state, &hw_config,
+					    &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_config & ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL;
+	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;
+	if (mode) {
+		switch (hw_config & ICE_AQC_GET_CGU_DPLL_CONFIG_MODE) {
+		case ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_FREERUN:
+			*mode = DPLL_MODE_FREERUN;
+			*dpll_state = DPLL_LOCK_STATUS_UNLOCKED;
+			return status;
+		case ICE_AQC_GET_CGU_DPLL_CONFIG_MODE_AUTOMATIC:
+			*mode = DPLL_MODE_AUTOMATIC;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* 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 = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
+		else
+			*dpll_state = DPLL_LOCK_STATUS_LOCKED;
+	} else if (last_dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ ||
+		   last_dpll_state == DPLL_LOCK_STATUS_HOLDOVER) {
+		*dpll_state = DPLL_LOCK_STATUS_HOLDOVER;
+	} else {
+		*dpll_state = DPLL_LOCK_STATUS_UNLOCKED;
+	}
+
+	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..b585bbadda91 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -3,6 +3,7 @@
 
 #ifndef _ICE_PTP_HW_H_
 #define _ICE_PTP_HW_H_
+#include <linux/dpll.h>
 
 enum ice_ptp_tmr_cmd {
 	INIT_TIME,
@@ -109,6 +110,222 @@ struct ice_cgu_pll_params_e822 {
 	u32 post_pll_div;
 };
 
+#define E810C_QSFP_C827_0_HANDLE	2
+#define E810C_QSFP_C827_1_HANDLE	3
+enum ice_e810_c827_idx {
+	C827_0,
+	C827_1
+};
+
+enum ice_phy_rclk_pins {
+	ICE_RCLKA_PIN = 0,		/* SCL pin */
+	ICE_RCLKB_PIN,			/* SDA pin */
+};
+
+#define ICE_E810_RCLK_PINS_NUM		(ICE_RCLKB_PIN + 1)
+#define ICE_E822_RCLK_PINS_NUM		(ICE_RCLKA_PIN + 1)
+#define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
+					  (_pin) + ZL_REF1P)
+enum ice_cgu_state {
+	ICE_CGU_STATE_UNKNOWN = -1,
+	ICE_CGU_STATE_INVALID,		/* state is not valid */
+	ICE_CGU_STATE_FREERUN,		/* clock is free-running */
+	ICE_CGU_STATE_LOCKED,		/* clock is locked to the reference,
+					 * but the holdover memory is not valid
+					 */
+	ICE_CGU_STATE_LOCKED_HO_ACQ,	/* clock is locked to the reference
+					 * and holdover memory is valid
+					 */
+	ICE_CGU_STATE_HOLDOVER,		/* clock is in holdover mode */
+	ICE_CGU_STATE_MAX
+};
+
+#define MAX_CGU_STATE_NAME_LEN		14
+struct ice_cgu_state_desc {
+	char name[MAX_CGU_STATE_NAME_LEN];
+	enum ice_cgu_state state;
+};
+
+enum ice_zl_cgu_in_pins {
+	ZL_REF0P = 0,
+	ZL_REF0N,
+	ZL_REF1P,
+	ZL_REF1N,
+	ZL_REF2P,
+	ZL_REF2N,
+	ZL_REF3P,
+	ZL_REF3N,
+	ZL_REF4P,
+	ZL_REF4N,
+	NUM_ZL_CGU_INPUT_PINS
+};
+
+enum ice_zl_cgu_out_pins {
+	ZL_OUT0 = 0,
+	ZL_OUT1,
+	ZL_OUT2,
+	ZL_OUT3,
+	ZL_OUT4,
+	ZL_OUT5,
+	ZL_OUT6,
+	NUM_ZL_CGU_OUTPUT_PINS
+};
+
+enum ice_si_cgu_in_pins {
+	SI_REF0P = 0,
+	SI_REF0N,
+	SI_REF1P,
+	SI_REF1N,
+	SI_REF2P,
+	SI_REF2N,
+	SI_REF3,
+	SI_REF4,
+	NUM_SI_CGU_INPUT_PINS
+};
+
+enum ice_si_cgu_out_pins {
+	SI_OUT0 = 0,
+	SI_OUT1,
+	SI_OUT2,
+	SI_OUT3,
+	SI_OUT4,
+	NUM_SI_CGU_OUTPUT_PINS
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_common[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+	DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_1_hz[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_10_mhz[] = {
+	DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+struct ice_cgu_pin_desc {
+	char *name;
+	u8 index;
+	enum dpll_pin_type type;
+	u32 freq_supp_num;
+	struct dpll_pin_frequency *freq_supp;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, 0, 0 },
+	{ "NONE",	  SI_REF0N, 0, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	  SI_REF2N, 0, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, 0, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+	{ "NONE",	  ZL_REF2P, 0, 0 },
+	{ "NONE",	  ZL_REF2N, 0, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	  ZL_REF3N, 0, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+	{ "NONE",	   ZL_OUT5, 0, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +414,20 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data);
 int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data);
 int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data);
 bool ice_is_pca9575_present(struct ice_hw *hw);
+bool ice_is_phy_rclk_present(struct ice_hw *hw);
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw);
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx);
+bool ice_is_cgu_present(struct ice_hw *hw);
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input);
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num);
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input);
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum dpll_lock_status last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum dpll_lock_status *dpll_state,
+		      enum dpll_mode *mode);
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);
 
 #define PFTSYN_SEM_BYTES	4
 
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index a09556e57803..128bc4d326f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -963,6 +963,7 @@ struct ice_hw {
 	DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX);
 	u8 dvm_ena;
 	u16 io_expander_handle;
+	u8 cgu_part_number;
 };
 
 /* Statistics collected by each port, VSI, VEB, and S-channel */
-- 
2.27.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] 109+ messages in thread

* [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:19   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- add support for DPLL_MODE_FREERUN mode
- use DPLL_LOCK_STATUS, remove ICE_DPLL_LOCK_STATUS
- fix mutex locking scheme
- remove rclk pin label
- fix/remmove struct fields descriptions

v8->v9:
- drop pointless 0 assignement
- ice_dpll_init(..) returns void instead of int
- fix context description of the functions
- fix ice_dpll_init(..) traces
- fix use package_label instead pf board_label for rclk pin
- be consistent on cgu presence naming
- remove indent in ice_dpll_deinit(..)
- remove unused struct field lock_err_num
- fix kworker resched behavior
- remove debug log from ice_dpll_deinit_worker(..)
- reorder ice internal functions
- release resources directly on error path
- remove redundant NULL checks when releasing resources
- do not assign NULL to pointers after releasing resources
- simplify variable assignement
- fix 'int ret;' declarations across the ice_dpll.c
- remove leftover ice_dpll_find(..)
- get pf pointer from dpll_priv without type cast
- improve error reporting
- fix documentation
- fix ice_dpll_update_state(..) flow
- fix return in case out of range prio set

 drivers/net/ethernet/intel/Kconfig        |    1 +
 drivers/net/ethernet/intel/ice/Makefile   |    3 +-
 drivers/net/ethernet/intel/ice/ice.h      |    3 +
 drivers/net/ethernet/intel/ice/ice_dpll.c | 2053 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |  104 ++
 drivers/net/ethernet/intel/ice/ice_main.c |    7 +
 6 files changed, 2170 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 9bc0a9519899..913dcf928d15 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -284,6 +284,7 @@ config ICE
 	select DIMLIB
 	select NET_DEVLINK
 	select PLDMFW
+	select DPLL
 	help
 	  This driver supports Intel(R) Ethernet Connection E800 Series of
 	  devices.  For more information on how to identify your adapter, go
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 817977e3039d..85d6366d1f5b 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -34,7 +34,8 @@ ice-y := ice_main.o	\
 	 ice_lag.o	\
 	 ice_ethtool.o  \
 	 ice_repr.o	\
-	 ice_tc_lib.o
+	 ice_tc_lib.o	\
+	 ice_dpll.o
 ice-$(CONFIG_PCI_IOV) +=	\
 	ice_sriov.o		\
 	ice_virtchnl.o		\
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 484d1d143174..a520141ef665 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -76,6 +76,7 @@
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
 #include "ice_irq.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -507,6 +508,7 @@ enum ice_pf_flags {
 	ICE_FLAG_UNPLUG_AUX_DEV,
 	ICE_FLAG_MTU_CHANGED,
 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
 	ICE_PF_FLAGS_NBITS		/* must be last */
 };
 
@@ -636,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..ba319cfb9167
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,2053 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_trace.h"
+#include <linux/dpll.h>
+
+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
+#define ICE_DPLL_LOCK_TRIES			1000
+#define ICE_DPLL_PIN_IDX_INVALID		0xff
+#define ICE_DPLL_RCLK_NUM_PER_PF		1
+
+/**
+ * enum ice_dpll_pin_type - enumerate ice pin types:
+ * @ICE_DPLL_PIN_INVALID: invalid pin type
+ * @ICE_DPLL_PIN_TYPE_INPUT: input pin
+ * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
+ * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
+ */
+enum ice_dpll_pin_type {
+	ICE_DPLL_PIN_INVALID,
+	ICE_DPLL_PIN_TYPE_INPUT,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+};
+
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_INPUT] = "input",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
+};
+
+/**
+ * ice_dpll_cb_lock - lock dplls mutex in callback context
+ * @pf: private board structure
+ * @extack: error reporting
+ *
+ * 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 is not initialized
+ */
+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack *extack)
+{
+	int i;
+
+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+			if (extack)
+				NL_SET_ERR_MSG(extack,
+					       "ice dpll not initialized");
+			return -EFAULT;
+		}
+		if (mutex_trylock(&pf->dplls.lock))
+			return 0;
+		usleep_range(100, 150);
+	}
+	if (extack)
+		NL_SET_ERR_MSG(extack, "was not able to acquire mutex");
+
+	return -EBUSY;
+}
+
+/**
+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
+ * @pf: private board structure
+ *
+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
+ */
+static void ice_dpll_cb_unlock(struct ice_pf *pf)
+{
+	mutex_unlock(&pf->dplls.lock);
+}
+
+/**
+ * ice_dpll_pin_freq_set - set pin's frequency
+ * @pf: private board structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being configured
+ * @freq: frequency to be set
+ * @extack: error reporting
+ *
+ * Set requested frequency on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error on AQ or wrong pin type given
+ */
+static int
+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+		      enum ice_dpll_pin_type pin_type, const u32 freq,
+		      struct netlink_ext_ack *extack)
+{
+	int ret;
+	u8 flags;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
+					       pin->flags[0], freq, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   freq, pin->idx);
+		return ret;
+	}
+	pin->freq = freq;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       const u32 frequency,
+		       struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_input_frequency_set - input pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_set - output pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+			      const struct dpll_device *dpll, void *dpll_priv,
+			      u64 frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       u64 *frequency, struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*frequency = p->freq;
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_input_frequency_get - input pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a input pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 *frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_get - output pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+			      const struct dpll_device *dpll, void *dpll_priv,
+			      u64 *frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_enable - enable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being enabled
+ * @extack: error reporting
+ *
+ * Enable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    enum ice_dpll_pin_type pin_type,
+		    struct netlink_ext_ack *extack)
+{
+	u8 flags = 0;
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to enable %s pin:%u\n",
+				   ret, ice_aq_str(hw->adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_disable - disable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being disabled
+ * @extack: error reporting
+ *
+ * Disable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		     enum ice_dpll_pin_type pin_type,
+		     struct netlink_ext_ack *extack)
+{
+	u8 flags = 0;
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to disable %s pin:%u\n",
+				   ret, ice_aq_str(hw->adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_state_update - update pin's state
+ * @pf: private board struct
+ * @pin: structure with pin attributes to be updated
+ * @pin_type: type of pin being updated
+ * @extack: error reporting
+ *
+ * Determine pin current state and frequency, then update struct
+ * holding the pin info. For input pin states are separated for each
+ * dpll, for rclk pins states are separated for each parent.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			  enum ice_dpll_pin_type pin_type,
+			  struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
+					       NULL, &pin->flags[0],
+					       &pin->freq, NULL);
+		if (ret)
+			goto err;
+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
+			if (pin->pin) {
+				pin->state[pf->dplls.eec.dpll_idx] =
+					pin->pin == pf->dplls.eec.active_input ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					pin->pin == pf->dplls.pps.active_input ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+			} else {
+				pin->state[pf->dplls.eec.dpll_idx] =
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					DPLL_PIN_STATE_SELECTABLE;
+			}
+		} else {
+			pin->state[pf->dplls.eec.dpll_idx] =
+				DPLL_PIN_STATE_DISCONNECTED;
+			pin->state[pf->dplls.pps.dpll_idx] =
+				DPLL_PIN_STATE_DISCONNECTED;
+		}
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
+						&pin->flags[0], NULL,
+						&pin->freq, NULL);
+		if (ret)
+			goto err;
+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
+		else
+			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
+		break;
+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+
+		for (parent = 0; parent < pf->dplls.rclk.num_parents;
+		     parent++) {
+			u8 p = parent;
+
+			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
+							 &port_num,
+							 &pin->flags[parent],
+							 NULL);
+			if (ret)
+				goto err;
+			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
+			    pin->flags[parent])
+				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
+			else
+				pin->state[parent] =
+					DPLL_PIN_STATE_DISCONNECTED;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+err:
+	if (extack)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to update %s pin:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+	else
+		dev_err_ratelimited(ice_pf_to_dev(pf),
+				    "err:%d %s failed to update %s pin:%u\n",
+				    ret,
+				    ice_aq_str(pf->hw.adminq.sq_last_status),
+				    pin_type_name[pin_type], pin->idx);
+	return ret;
+}
+
+/**
+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
+ * @pf: board private structure
+ * @dpll: ice dpll pointer
+ * @pin: ice pin pointer
+ * @prio: priority value being set on a dpll
+ * @extack: error reporting
+ *
+ * Internal wrapper for setting the priority in the hardware.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
+			   struct ice_dpll_pin *pin, const u32 prio,
+			   struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
+				      (u8)prio);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "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
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @status: on success holds dpll's lock status
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback, provides dpll's lock status.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_lock_status *status,
+			 struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*status = d->dpll_state;
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_supported - check if dpll's working mode is supported
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: mode to be checked for support
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides information if working mode is supported
+ * by dpll.
+ *
+ * Return:
+ * * true - mode is supported
+ * * false - mode is not supported
+ */
+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
+				    void *dpll_priv,
+				    enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	if (mode == DPLL_MODE_AUTOMATIC ||
+	    mode == DPLL_MODE_FREERUN)
+		return true;
+
+	return false;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: on success holds current working mode of dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_mode *mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*mode = d->mode;
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_mode_set - set dpll's working mode
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: requested working mode of dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. User requests working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_mode mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	u8 config;
+	int ret;
+
+	switch (mode) {
+	case DPLL_MODE_AUTOMATIC:
+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC;
+		break;
+	case DPLL_MODE_FREERUN:
+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_aq_set_cgu_dpll_config(&pf->hw, d->dpll_idx, d->ref_state,
+					 config, d->eec_mode);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set mode:%u on dpll:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   mode, d->dpll_idx);
+	else
+		d->mode = mode;
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_state_set - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @enable: if pin shalll be enabled
+ * @extack: error reporting
+ * @pin_type: type of a pin
+ *
+ * Set pin state on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       bool enable, struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	if (enable)
+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type, extack);
+	else
+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type, extack);
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Set given state on output type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
+
+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Enables given mode on input type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_state state,
+			 struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_SELECTABLE;
+
+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_pin_state_get - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ * @pin_type: type of questioned pin
+ *
+ * Determine pin state set it on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
+	if (ret)
+		goto unlock;
+	if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
+		*state = p->state[d->dpll_idx];
+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
+		*state = p->state[0];
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_state_get - get output pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_state *state,
+			  struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_get - get input pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_state *state,
+			 struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_input_prio_get - get dpll's input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: on success - returns input priority on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			u32 *prio, struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*prio = d->input_prio[p->idx];
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_input_prio_set - set dpll input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: input priority to be set on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			u32 prio, struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	if (prio > ICE_DPLL_PRIO_MAX) {
+		NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
+				   ICE_DPLL_PRIO_MAX);
+		return -EINVAL;
+	}
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_input_direction - callback for get input pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds input pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a input pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_direction *direction,
+			 struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_INPUT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_output_direction - callback for get output pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds output pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of an output pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_direction *direction,
+			  struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_OUTPUT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: parent private data pointer passed on pin registration
+ * @state: state to be set on pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
+			       const struct dpll_pin *parent_pin,
+			       void *parent_pin_priv,
+			       enum dpll_pin_state state,
+			       struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
+	struct ice_pf *pf = p->pf;
+	u32 hw_idx;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+	if (hw_idx >= pf->dplls.num_inputs)
+		goto unlock;
+
+	if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
+	    (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "pin:%u state:%u on parent:%u already set",
+				   p->idx, state, parent->idx);
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
+					 &p->freq);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set pin state:%u for pin:%u on parent:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   state, p->idx, parent->idx);
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: pin parent priv data pointer passed on pin registration
+ * @state: on success holds pin state on parent pin
+ * @extack: error reporting
+ *
+ * dpll subsystem callback, get a state of a recovered clock pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
+			       const struct dpll_pin *parent_pin,
+			       void *parent_pin_priv,
+			       enum dpll_pin_state *state,
+			       struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+	struct ice_pf *pf = p->pf;
+	u32 hw_idx;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+	if (hw_idx >= pf->dplls.num_inputs)
+		goto unlock;
+
+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+					extack);
+	if (ret)
+		goto unlock;
+
+	*state = p->state[hw_idx];
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
+	.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
+	.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
+	.direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_input_ops = {
+	.frequency_get = ice_dpll_input_frequency_get,
+	.frequency_set = ice_dpll_input_frequency_set,
+	.state_on_dpll_get = ice_dpll_input_state_get,
+	.state_on_dpll_set = ice_dpll_input_state_set,
+	.prio_get = ice_dpll_input_prio_get,
+	.prio_set = ice_dpll_input_prio_set,
+	.direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_output_ops = {
+	.frequency_get = ice_dpll_output_frequency_get,
+	.frequency_set = ice_dpll_output_frequency_set,
+	.state_on_dpll_get = ice_dpll_output_state_get,
+	.state_on_dpll_set = ice_dpll_output_state_set,
+	.direction_get = ice_dpll_output_direction,
+};
+
+static const struct dpll_device_ops ice_dpll_ops = {
+	.lock_status_get = ice_dpll_lock_status_get,
+	.mode_supported = ice_dpll_mode_supported,
+	.mode_get = ice_dpll_mode_get,
+	.mode_set = ice_dpll_mode_set,
+};
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ *
+ * Return: generated clock id for the board
+ */
+static u64 ice_generate_clock_id(struct ice_pf *pf)
+{
+	return pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_notify_changes - notify dpll subsystem about changes
+ * @d: pointer do dpll
+ *
+ * Once change detected appropriate event is submitted to the dpll subsystem.
+ */
+static void ice_dpll_notify_changes(struct ice_dpll *d)
+{
+	if (d->prev_dpll_state != d->dpll_state) {
+		d->prev_dpll_state = d->dpll_state;
+		dpll_device_change_ntf(d->dpll);
+	}
+	if (d->prev_input != d->active_input) {
+		if (d->prev_input)
+			dpll_pin_change_ntf(d->prev_input);
+		d->prev_input = d->active_input;
+		if (d->active_input)
+			dpll_pin_change_ntf(d->active_input);
+	}
+}
+
+/**
+ * ice_dpll_update_state - update dpll state
+ * @pf: pf private structure
+ * @d: pointer to queried dpll device
+ * @init: if function called on initialization of ice dpll
+ *
+ * Poll current state of dpll from hw and update ice_dpll struct.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - AQ failure
+ */
+static int
+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
+{
+	struct ice_dpll_pin *p = NULL;
+	int ret;
+
+	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
+				&d->input_idx, &d->ref_state, &d->eec_mode,
+				&d->phase_shift, &d->dpll_state, &d->mode);
+
+	dev_dbg(ice_pf_to_dev(pf),
+		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
+		d->dpll_idx, d->prev_input_idx, d->input_idx,
+		d->dpll_state, d->prev_dpll_state, d->mode);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(pf->hw.adminq.sq_last_status));
+		return ret;
+	}
+	if (init) {
+		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
+		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
+			d->active_input = pf->dplls.inputs[d->input_idx].pin;
+		p = &pf->dplls.inputs[d->input_idx];
+		return ice_dpll_pin_state_update(pf, p,
+						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
+	}
+	if (d->dpll_state == DPLL_LOCK_STATUS_HOLDOVER ||
+	    d->dpll_state == DPLL_LOCK_STATUS_UNLOCKED) {
+		d->active_input = NULL;
+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID)
+			p = &pf->dplls.inputs[d->input_idx];
+		d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
+		d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
+		if (!p)
+			return 0;
+		ret = ice_dpll_pin_state_update(pf, p,
+						ICE_DPLL_PIN_TYPE_INPUT, NULL);
+	} else if (d->input_idx != d->prev_input_idx) {
+		if (d->prev_input_idx != ICE_DPLL_PIN_IDX_INVALID) {
+			p = &pf->dplls.inputs[d->prev_input_idx];
+			ice_dpll_pin_state_update(pf, p,
+						  ICE_DPLL_PIN_TYPE_INPUT,
+						  NULL);
+		}
+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID) {
+			p = &pf->dplls.inputs[d->input_idx];
+			d->active_input = p->pin;
+			ice_dpll_pin_state_update(pf, p,
+						  ICE_DPLL_PIN_TYPE_INPUT,
+						  NULL);
+		}
+		d->prev_input_idx = d->input_idx;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ * Context: Holds pf->dplls.lock
+ */
+static void ice_dpll_periodic_work(struct kthread_work *work)
+{
+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, NULL);
+	if (ret == -EBUSY)
+		goto resched;
+	else if (ret)
+		return;
+	ret = ice_dpll_update_state(pf, de, false);
+	if (!ret)
+		ret = ice_dpll_update_state(pf, dp, false);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
+			dev_err(ice_pf_to_dev(pf),
+				"EEC/PPS DPLLs periodic work disabled\n");
+			return;
+		}
+	}
+	ice_dpll_cb_unlock(pf);
+	ice_dpll_notify_changes(de);
+	ice_dpll_notify_changes(dp);
+
+resched:
+	/* Run twice a second or reschedule if update failed */
+	kthread_queue_delayed_work(d->kworker, &d->work,
+				   ret ? msecs_to_jiffies(10) :
+				   msecs_to_jiffies(500));
+}
+
+/**
+ * ice_dpll_release_pins - release pins resources from dpll subsystem
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Release resources of given pins array in the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		dpll_pin_put(pins[i].pin);
+}
+
+/**
+ * ice_dpll_get_pins - get pins from dpll subsystem
+ * @pf: board private structure
+ * @pins: pointer to pins array
+ * @start_idx: get starts from this pin idx value
+ * @count: number of pins
+ * @clock_id: clock_id of dpll device
+ *
+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
+ * pins array.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - allocation failure reason
+ */
+static int
+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
+		  int start_idx, int count, u64 clock_id)
+{
+	int i, ret;
+
+	for (i = 0; i < count; i++) {
+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
+					   &pins[i].prop);
+		if (IS_ERR(pins[i].pin)) {
+			ret = PTR_ERR(pins[i].pin);
+			goto release_pins;
+		}
+	}
+
+	return 0;
+
+release_pins:
+	while (--i >= 0)
+		dpll_pin_put(pins[i].pin);
+	return ret;
+}
+
+/**
+ * ice_dpll_unregister_pins - unregister pins from a dpll
+ * @dpll: dpll device pointer
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ *
+ * Unregister pins of a given array of pins from given dpll device registered in
+ * dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+			 const struct dpll_pin_ops *ops, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @dpll: dpll pointer to register pins with
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ *
+ * Register pins of a given array with given dpll in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+		       const struct dpll_pin_ops *ops, int count)
+{
+	int ret, i;
+
+	for (i = 0; i < count; i++) {
+		ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
+		if (ret)
+			goto unregister_pins;
+	}
+
+	return 0;
+
+unregister_pins:
+	while (--i >= 0)
+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Context: Called under pf->dplls.lock
+ * If cgu is owned unregister pins from given dplls.
+ * Release pins resources to the dpll subsystem.
+ */
+static void
+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
+			    const struct dpll_pin_ops *ops,
+			    struct dpll_device *first,
+			    struct dpll_device *second)
+{
+	if (cgu) {
+		ice_dpll_unregister_pins(first, pins, ops, count);
+		ice_dpll_unregister_pins(second, pins, ops, count);
+	}
+	ice_dpll_release_pins(pins, count);
+}
+
+/**
+ * ice_dpll_init_direct_pins - initialize direct pins
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Allocate directly connected pins of a given array in dpll subsystem.
+ * If cgu is owned register allocated pins with given dplls.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
+			  struct ice_dpll_pin *pins, int start_idx, int count,
+			  const struct dpll_pin_ops *ops,
+			  struct dpll_device *first, struct dpll_device *second)
+{
+	int ret;
+
+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
+	if (ret)
+		return ret;
+	if (cgu) {
+		ret = ice_dpll_register_pins(first, pins, ops, count);
+		if (ret)
+			goto release_pins;
+		ret = ice_dpll_register_pins(second, pins, ops, count);
+		if (ret)
+			goto unregister_first;
+	}
+
+	return 0;
+
+unregister_first:
+	ice_dpll_unregister_pins(first, pins, ops, count);
+release_pins:
+	ice_dpll_release_pins(pins, count);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
+ * @pf: board private structure
+ *
+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
+	struct dpll_pin *parent;
+	int i;
+
+	for (i = 0; i < rclk->num_parents; i++) {
+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
+		if (!parent)
+			continue;
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, rclk);
+	}
+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
+		return;
+	netdev_dpll_pin_clear(vsi->netdev);
+	dpll_pin_put(rclk->pin);
+}
+
+/**
+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
+ * @pf: board private structure
+ * @pin: pin to register
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @ops: callback ops registered with the pins
+ *
+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
+ * pin with the parents it has in the info. Register pin with the pf's main vsi
+ * netdev.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			int start_idx, const struct dpll_pin_ops *ops)
+{
+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
+	struct dpll_pin *parent;
+	int ret, i;
+
+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
+				pf->dplls.clock_id);
+	if (ret)
+		return ret;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
+		if (!parent) {
+			ret = -ENODEV;
+			goto unregister_pins;
+		}
+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
+					       ops, &pf->dplls.rclk);
+		if (ret)
+			goto unregister_pins;
+	}
+	if (WARN_ON((!vsi || !vsi->netdev)))
+		return -EINVAL;
+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
+
+	return 0;
+
+unregister_pins:
+	while (i) {
+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
+	}
+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_pins - deinitialize direct pins
+ * @pf: board private structure
+ * @cgu: if cgu is controlled by this pf
+ *
+ * If cgu is owned unregister directly connected pins from the dplls.
+ * Release resources of directly connected pins from the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
+{
+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
+	int num_outputs = pf->dplls.num_outputs;
+	int num_inputs = pf->dplls.num_inputs;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+
+	ice_dpll_deinit_rclk_pin(pf);
+	if (cgu) {
+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
+					 num_inputs);
+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
+					 num_inputs);
+	}
+	ice_dpll_release_pins(inputs, num_inputs);
+	if (cgu) {
+		ice_dpll_unregister_pins(dp->dpll, outputs,
+					 &ice_dpll_output_ops, num_outputs);
+		ice_dpll_unregister_pins(de->dpll, outputs,
+					 &ice_dpll_output_ops, num_outputs);
+		ice_dpll_release_pins(outputs, num_outputs);
+	}
+}
+
+/**
+ * ice_dpll_init_pins - init pins and register pins with a dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
+ * subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
+{
+	u32 rclk_idx;
+	int ret;
+
+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
+					pf->dplls.num_inputs,
+					&ice_dpll_input_ops,
+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+	if (ret)
+		return ret;
+	if (cgu) {
+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
+						pf->dplls.num_inputs,
+						pf->dplls.num_outputs,
+						&ice_dpll_output_ops,
+						pf->dplls.eec.dpll,
+						pf->dplls.pps.dpll);
+		if (ret)
+			goto deinit_inputs;
+	}
+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
+				      &ice_dpll_rclk_ops);
+	if (ret)
+		goto deinit_outputs;
+
+	return 0;
+deinit_outputs:
+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
+				    pf->dplls.num_outputs,
+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
+deinit_inputs:
+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_dpll - deinitialize dpll device
+ * @pf: board private structure
+ * @d: pointer to ice_dpll
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * If cgu is owned unregister the dpll from dpll subsystem.
+ * Release resources of dpll device from dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
+{
+	if (cgu)
+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
+	dpll_device_put(d->dpll);
+}
+
+/**
+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
+ * @pf: board private structure
+ * @d: dpll to be initialized
+ * @cgu: if cgu is present and controlled by this NIC
+ * @type: type of dpll being initialized
+ *
+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
+ * by this NIC, register dpll with the callback ops.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int
+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
+		   enum dpll_type type)
+{
+	u64 clock_id = pf->dplls.clock_id;
+	int ret;
+
+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
+	if (IS_ERR(d->dpll)) {
+		ret = PTR_ERR(d->dpll);
+		dev_err(ice_pf_to_dev(pf),
+			"dpll_device_get failed (%p) err=%d\n", d, ret);
+		return ret;
+	}
+	d->pf = pf;
+	if (cgu) {
+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
+		if (ret) {
+			dpll_device_put(d->dpll);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_deinit_worker - deinitialize dpll kworker
+ * @pf: board private structure
+ *
+ * Stop dpll's kworker, release it's resources.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+
+	kthread_cancel_delayed_work_sync(&d->work);
+	kthread_destroy_worker(d->kworker);
+}
+
+/**
+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
+ * @pf: board private structure
+ *
+ * Create and start DPLLs periodic worker.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - create worker failure
+ */
+static int ice_dpll_init_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct kthread_worker *kworker;
+
+	ice_dpll_update_state(pf, &d->eec, true);
+	ice_dpll_update_state(pf, &d->pps, true);
+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
+	kworker = kthread_create_worker(0, "ice-dplls-%s",
+					dev_name(ice_pf_to_dev(pf)));
+	if (IS_ERR(kworker))
+		return PTR_ERR(kworker);
+	d->kworker = kworker;
+	d->cgu_state_acq_err_num = 0;
+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_init_info_direct_pins - initializes direct pins info
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information for directly connected pins, cache them in pf's pins
+ * structures.
+ *
+ * Context: Called under pf->dplls.lock.
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
+			       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
+	struct ice_hw *hw = &pf->hw;
+	struct ice_dpll_pin *pins;
+	int num_pins, i, ret;
+	u8 freq_supp_num;
+	bool input;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+		input = true;
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+		input = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
+		if (input) {
+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
+						      &de->input_prio[i]);
+			if (ret)
+				return ret;
+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
+						      &dp->input_prio[i]);
+			if (ret)
+				return ret;
+			pins[i].prop.capabilities |=
+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
+		}
+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
+		if (ret)
+			return ret;
+		pins[i].prop.freq_supported =
+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
+		pins[i].prop.freq_supported_num = freq_supp_num;
+		pins[i].pf = pf;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
+ * @pf: board private structure
+ *
+ * Init information for rclk pin, cache them in pf->dplls.rclk.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
+
+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+	pin->pf = pf;
+
+	return ice_dpll_pin_state_update(pf, pin,
+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
+}
+
+/**
+ * ice_dpll_init_pins_info - init pins info wrapper
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Wraps functions for pin initialization.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
+{
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		return ice_dpll_init_info_direct_pins(pf, pin_type);
+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+		return ice_dpll_init_info_rclk_pin(pf);
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * ice_dpll_deinit_info - release memory allocated for pins info
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_info(struct ice_pf *pf)
+{
+	kfree(pf->dplls.inputs);
+	kfree(pf->dplls.outputs);
+	kfree(pf->dplls.eec.input_prio);
+	kfree(pf->dplls.pps.input_prio);
+}
+
+/**
+ * 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).
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
+{
+	struct ice_aqc_get_cgu_abilities abilities;
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_hw *hw = &pf->hw;
+	int ret, alloc_size, i;
+
+	d->clock_id = ice_generate_clock_id(pf);
+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"err:%d %s failed to read cgu abilities\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status));
+		return ret;
+	}
+
+	de->dpll_idx = abilities.eec_dpll_idx;
+	dp->dpll_idx = abilities.pps_dpll_idx;
+	d->num_inputs = abilities.num_inputs;
+	d->num_outputs = abilities.num_outputs;
+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
+
+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->inputs)
+		return -ENOMEM;
+
+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!de->input_prio)
+		return -ENOMEM;
+
+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!dp->input_prio)
+		return -ENOMEM;
+
+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
+	if (ret)
+		goto deinit_info;
+
+	if (cgu) {
+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+		if (!d->outputs)
+			goto deinit_info;
+
+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+		if (ret)
+			goto deinit_info;
+	}
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
+					&pf->dplls.rclk.num_parents);
+	if (ret)
+		return ret;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+	if (ret)
+		return ret;
+	de->mode = DPLL_MODE_AUTOMATIC;
+	dp->mode = DPLL_MODE_AUTOMATIC;
+
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
+
+	return 0;
+
+deinit_info:
+	dev_err(ice_pf_to_dev(pf),
+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
+		__func__, d->inputs, de->input_prio,
+		dp->input_prio, d->outputs);
+	ice_dpll_deinit_info(pf);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * Handles the cleanup work required after dpll initialization,freeing resources
+ * and unregistering the dpll, pin and all resources used for handling them.
+ *
+ * Context: Function holds pf->dplls.lock mutex.
+ */
+void ice_dpll_deinit(struct ice_pf *pf)
+{
+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
+
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+		return;
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+
+	ice_dpll_deinit_pins(pf, cgu);
+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+	ice_dpll_deinit_info(pf);
+	if (cgu)
+		ice_dpll_deinit_worker(pf);
+	mutex_destroy(&pf->dplls.lock);
+}
+
+/**
+ * ice_dpll_init - initialize support for dpll subsystem
+ * @pf: board private structure
+ *
+ * Set up the device dplls, register them and pins connected within Linux dpll
+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
+ * configuration requests.
+ *
+ * Context: Function initializes and holds pf->dplls.lock mutex.
+ */
+void ice_dpll_init(struct ice_pf *pf)
+{
+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
+	struct ice_dplls *d = &pf->dplls;
+	int err = 0;
+
+	err = ice_dpll_init_info(pf, cgu);
+	if (err)
+		goto err_exit;
+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
+	if (err)
+		goto deinit_info;
+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
+	if (err)
+		goto deinit_eec;
+	err = ice_dpll_init_pins(pf, cgu);
+	if (err)
+		goto deinit_pps;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	if (cgu) {
+		err = ice_dpll_init_worker(pf);
+		if (err)
+			goto deinit_pins;
+	}
+
+	return;
+
+deinit_pins:
+	ice_dpll_deinit_pins(pf, cgu);
+deinit_pps:
+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+deinit_eec:
+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+deinit_info:
+	ice_dpll_deinit_info(pf);
+err_exit:
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+	mutex_unlock(&d->lock);
+	mutex_destroy(&d->lock);
+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022, Intel Corporation. */
+
+#ifndef _ICE_DPLL_H_
+#define _ICE_DPLL_H_
+
+#include "ice.h"
+
+#define ICE_DPLL_PRIO_MAX	0xF
+#define ICE_DPLL_RCLK_NUM_MAX	4
+
+/** ice_dpll_pin - store info about pins
+ * @pin: dpll pin structure
+ * @pf: pointer to pf, which has registered the dpll_pin
+ * @idx: ice pin private idx
+ * @num_parents: hols number of parent pins
+ * @parent_idx: hold indexes of parent pins
+ * @flags: pin flags returned from HW
+ * @state: state of a pin
+ * @prop: pin properities
+ * @freq: current frequency of a pin
+ */
+struct ice_dpll_pin {
+	struct dpll_pin *pin;
+	struct ice_pf *pf;
+	u8 idx;
+	u8 num_parents;
+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
+	struct dpll_pin_properties prop;
+	u32 freq;
+};
+
+/** ice_dpll - store info required for DPLL control
+ * @dpll: pointer to dpll dev
+ * @pf: pointer to pf, which has registered the dpll_device
+ * @dpll_idx: index of dpll on the NIC
+ * @input_idx: currently selected input index
+ * @prev_input_idx: previously selected input index
+ * @ref_state: state of dpll reference signals
+ * @eec_mode: eec_mode dpll is configured for
+ * @phase_shift: phase shift delay of a dpll
+ * @input_prio: priorities of each input
+ * @dpll_state: current dpll sync state
+ * @prev_dpll_state: last dpll sync state
+ * @active_input: pointer to active input pin
+ * @prev_input: pointer to previous active input pin
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	struct ice_pf *pf;
+	u8 dpll_idx;
+	u8 input_idx;
+	u8 prev_input_idx;
+	u8 ref_state;
+	u8 eec_mode;
+	s64 phase_shift;
+	u8 *input_prio;
+	enum dpll_lock_status dpll_state;
+	enum dpll_lock_status prev_dpll_state;
+	enum dpll_mode mode;
+	struct dpll_pin *active_input;
+	struct dpll_pin *prev_input;
+};
+
+/** ice_dplls - store info required for CCU (clock controlling unit)
+ * @kworker: periodic worker
+ * @work: periodic work
+ * @lock: locks access to configuration of a dpll
+ * @eec: pointer to EEC dpll dev
+ * @pps: pointer to PPS dpll dev
+ * @inputs: input pins pointer
+ * @outputs: output pins pointer
+ * @rclk: recovered pins pointer
+ * @num_inputs: number of input pins available on dpll
+ * @num_outputs: number of output pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ * @base_rclk_idx: idx of first pin used for clock revocery pins
+ * @clock_id: clock_id of dplls
+ */
+struct ice_dplls {
+	struct kthread_worker *kworker;
+	struct kthread_delayed_work work;
+	struct mutex lock;
+	struct ice_dpll eec;
+	struct ice_dpll pps;
+	struct ice_dpll_pin *inputs;
+	struct ice_dpll_pin *outputs;
+	struct ice_dpll_pin rclk;
+	u8 num_inputs;
+	u8 num_outputs;
+	int cgu_state_acq_err_num;
+	u8 base_rclk_idx;
+	u64 clock_id;
+	s32 input_phase_adj_max;
+	s32 output_phase_adj_max;
+};
+
+void ice_dpll_init(struct ice_pf *pf);
+
+void ice_dpll_deinit(struct ice_pf *pf);
+
+#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 19a5e7f3a075..0a94daaf3d20 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4613,6 +4613,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");
@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
 		ice_gnss_exit(pf);
 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
 		ice_ptp_release(pf);
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
+	    ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_deinit(pf);
 }
 
 static void ice_init_wakeup(struct ice_pf *pf)
-- 
2.27.0


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

* [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-20  9:19   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Milena Olech, Michal Michalik, Vadim Fedorenko, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
RFC v9->v0:
- add support for DPLL_MODE_FREERUN mode
- use DPLL_LOCK_STATUS, remove ICE_DPLL_LOCK_STATUS
- fix mutex locking scheme
- remove rclk pin label
- fix/remmove struct fields descriptions

v8->v9:
- drop pointless 0 assignement
- ice_dpll_init(..) returns void instead of int
- fix context description of the functions
- fix ice_dpll_init(..) traces
- fix use package_label instead pf board_label for rclk pin
- be consistent on cgu presence naming
- remove indent in ice_dpll_deinit(..)
- remove unused struct field lock_err_num
- fix kworker resched behavior
- remove debug log from ice_dpll_deinit_worker(..)
- reorder ice internal functions
- release resources directly on error path
- remove redundant NULL checks when releasing resources
- do not assign NULL to pointers after releasing resources
- simplify variable assignement
- fix 'int ret;' declarations across the ice_dpll.c
- remove leftover ice_dpll_find(..)
- get pf pointer from dpll_priv without type cast
- improve error reporting
- fix documentation
- fix ice_dpll_update_state(..) flow
- fix return in case out of range prio set

 drivers/net/ethernet/intel/Kconfig        |    1 +
 drivers/net/ethernet/intel/ice/Makefile   |    3 +-
 drivers/net/ethernet/intel/ice/ice.h      |    3 +
 drivers/net/ethernet/intel/ice/ice_dpll.c | 2053 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |  104 ++
 drivers/net/ethernet/intel/ice/ice_main.c |    7 +
 6 files changed, 2170 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 9bc0a9519899..913dcf928d15 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -284,6 +284,7 @@ config ICE
 	select DIMLIB
 	select NET_DEVLINK
 	select PLDMFW
+	select DPLL
 	help
 	  This driver supports Intel(R) Ethernet Connection E800 Series of
 	  devices.  For more information on how to identify your adapter, go
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 817977e3039d..85d6366d1f5b 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -34,7 +34,8 @@ ice-y := ice_main.o	\
 	 ice_lag.o	\
 	 ice_ethtool.o  \
 	 ice_repr.o	\
-	 ice_tc_lib.o
+	 ice_tc_lib.o	\
+	 ice_dpll.o
 ice-$(CONFIG_PCI_IOV) +=	\
 	ice_sriov.o		\
 	ice_virtchnl.o		\
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 484d1d143174..a520141ef665 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -76,6 +76,7 @@
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
 #include "ice_irq.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -507,6 +508,7 @@ enum ice_pf_flags {
 	ICE_FLAG_UNPLUG_AUX_DEV,
 	ICE_FLAG_MTU_CHANGED,
 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
 	ICE_PF_FLAGS_NBITS		/* must be last */
 };
 
@@ -636,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..ba319cfb9167
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,2053 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_trace.h"
+#include <linux/dpll.h>
+
+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
+#define ICE_DPLL_LOCK_TRIES			1000
+#define ICE_DPLL_PIN_IDX_INVALID		0xff
+#define ICE_DPLL_RCLK_NUM_PER_PF		1
+
+/**
+ * enum ice_dpll_pin_type - enumerate ice pin types:
+ * @ICE_DPLL_PIN_INVALID: invalid pin type
+ * @ICE_DPLL_PIN_TYPE_INPUT: input pin
+ * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
+ * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
+ */
+enum ice_dpll_pin_type {
+	ICE_DPLL_PIN_INVALID,
+	ICE_DPLL_PIN_TYPE_INPUT,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+};
+
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_INPUT] = "input",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
+};
+
+/**
+ * ice_dpll_cb_lock - lock dplls mutex in callback context
+ * @pf: private board structure
+ * @extack: error reporting
+ *
+ * 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 is not initialized
+ */
+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack *extack)
+{
+	int i;
+
+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+			if (extack)
+				NL_SET_ERR_MSG(extack,
+					       "ice dpll not initialized");
+			return -EFAULT;
+		}
+		if (mutex_trylock(&pf->dplls.lock))
+			return 0;
+		usleep_range(100, 150);
+	}
+	if (extack)
+		NL_SET_ERR_MSG(extack, "was not able to acquire mutex");
+
+	return -EBUSY;
+}
+
+/**
+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
+ * @pf: private board structure
+ *
+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
+ */
+static void ice_dpll_cb_unlock(struct ice_pf *pf)
+{
+	mutex_unlock(&pf->dplls.lock);
+}
+
+/**
+ * ice_dpll_pin_freq_set - set pin's frequency
+ * @pf: private board structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being configured
+ * @freq: frequency to be set
+ * @extack: error reporting
+ *
+ * Set requested frequency on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error on AQ or wrong pin type given
+ */
+static int
+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+		      enum ice_dpll_pin_type pin_type, const u32 freq,
+		      struct netlink_ext_ack *extack)
+{
+	int ret;
+	u8 flags;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
+					       pin->flags[0], freq, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   freq, pin->idx);
+		return ret;
+	}
+	pin->freq = freq;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       const u32 frequency,
+		       struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_input_frequency_set - input pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_set - output pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+			      const struct dpll_device *dpll, void *dpll_priv,
+			      u64 frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       u64 *frequency, struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*frequency = p->freq;
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_input_frequency_get - input pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a input pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+			     const struct dpll_device *dpll, void *dpll_priv,
+			     u64 *frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_get - output pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+			      const struct dpll_device *dpll, void *dpll_priv,
+			      u64 *frequency, struct netlink_ext_ack *extack)
+{
+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_enable - enable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being enabled
+ * @extack: error reporting
+ *
+ * Enable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    enum ice_dpll_pin_type pin_type,
+		    struct netlink_ext_ack *extack)
+{
+	u8 flags = 0;
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to enable %s pin:%u\n",
+				   ret, ice_aq_str(hw->adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_disable - disable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being disabled
+ * @extack: error reporting
+ *
+ * Disable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		     enum ice_dpll_pin_type pin_type,
+		     struct netlink_ext_ack *extack)
+{
+	u8 flags = 0;
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to disable %s pin:%u\n",
+				   ret, ice_aq_str(hw->adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_state_update - update pin's state
+ * @pf: private board struct
+ * @pin: structure with pin attributes to be updated
+ * @pin_type: type of pin being updated
+ * @extack: error reporting
+ *
+ * Determine pin current state and frequency, then update struct
+ * holding the pin info. For input pin states are separated for each
+ * dpll, for rclk pins states are separated for each parent.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			  enum ice_dpll_pin_type pin_type,
+			  struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
+					       NULL, &pin->flags[0],
+					       &pin->freq, NULL);
+		if (ret)
+			goto err;
+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
+			if (pin->pin) {
+				pin->state[pf->dplls.eec.dpll_idx] =
+					pin->pin == pf->dplls.eec.active_input ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					pin->pin == pf->dplls.pps.active_input ?
+					DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_SELECTABLE;
+			} else {
+				pin->state[pf->dplls.eec.dpll_idx] =
+					DPLL_PIN_STATE_SELECTABLE;
+				pin->state[pf->dplls.pps.dpll_idx] =
+					DPLL_PIN_STATE_SELECTABLE;
+			}
+		} else {
+			pin->state[pf->dplls.eec.dpll_idx] =
+				DPLL_PIN_STATE_DISCONNECTED;
+			pin->state[pf->dplls.pps.dpll_idx] =
+				DPLL_PIN_STATE_DISCONNECTED;
+		}
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
+						&pin->flags[0], NULL,
+						&pin->freq, NULL);
+		if (ret)
+			goto err;
+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
+		else
+			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
+		break;
+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+
+		for (parent = 0; parent < pf->dplls.rclk.num_parents;
+		     parent++) {
+			u8 p = parent;
+
+			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
+							 &port_num,
+							 &pin->flags[parent],
+							 NULL);
+			if (ret)
+				goto err;
+			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
+			    pin->flags[parent])
+				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
+			else
+				pin->state[parent] =
+					DPLL_PIN_STATE_DISCONNECTED;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+err:
+	if (extack)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to update %s pin:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   pin_type_name[pin_type], pin->idx);
+	else
+		dev_err_ratelimited(ice_pf_to_dev(pf),
+				    "err:%d %s failed to update %s pin:%u\n",
+				    ret,
+				    ice_aq_str(pf->hw.adminq.sq_last_status),
+				    pin_type_name[pin_type], pin->idx);
+	return ret;
+}
+
+/**
+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
+ * @pf: board private structure
+ * @dpll: ice dpll pointer
+ * @pin: ice pin pointer
+ * @prio: priority value being set on a dpll
+ * @extack: error reporting
+ *
+ * Internal wrapper for setting the priority in the hardware.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
+			   struct ice_dpll_pin *pin, const u32 prio,
+			   struct netlink_ext_ack *extack)
+{
+	int ret;
+
+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
+				      (u8)prio);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "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
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @status: on success holds dpll's lock status
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback, provides dpll's lock status.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_lock_status *status,
+			 struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*status = d->dpll_state;
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_supported - check if dpll's working mode is supported
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: mode to be checked for support
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides information if working mode is supported
+ * by dpll.
+ *
+ * Return:
+ * * true - mode is supported
+ * * false - mode is not supported
+ */
+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
+				    void *dpll_priv,
+				    enum dpll_mode mode,
+				    struct netlink_ext_ack *extack)
+{
+	if (mode == DPLL_MODE_AUTOMATIC ||
+	    mode == DPLL_MODE_FREERUN)
+		return true;
+
+	return false;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: on success holds current working mode of dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_mode *mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*mode = d->mode;
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_mode_set - set dpll's working mode
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @mode: requested working mode of dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. User requests working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_mode mode,
+			     struct netlink_ext_ack *extack)
+{
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	u8 config;
+	int ret;
+
+	switch (mode) {
+	case DPLL_MODE_AUTOMATIC:
+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC;
+		break;
+	case DPLL_MODE_FREERUN:
+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_aq_set_cgu_dpll_config(&pf->hw, d->dpll_idx, d->ref_state,
+					 config, d->eec_mode);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set mode:%u on dpll:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   mode, d->dpll_idx);
+	else
+		d->mode = mode;
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_state_set - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @enable: if pin shalll be enabled
+ * @extack: error reporting
+ * @pin_type: type of a pin
+ *
+ * Set pin state on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       bool enable, struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	if (enable)
+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type, extack);
+	else
+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type, extack);
+	if (!ret)
+		ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Set given state on output type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_state state,
+			  struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
+
+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Enables given mode on input type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_state state,
+			 struct netlink_ext_ack *extack)
+{
+	bool enable = state == DPLL_PIN_STATE_SELECTABLE;
+
+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_pin_state_get - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ * @pin_type: type of questioned pin
+ *
+ * Determine pin state set it on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
+		       const struct dpll_device *dpll, void *dpll_priv,
+		       enum dpll_pin_state *state,
+		       struct netlink_ext_ack *extack,
+		       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
+	if (ret)
+		goto unlock;
+	if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
+		*state = p->state[d->dpll_idx];
+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
+		*state = p->state[0];
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_state_get - get output pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_state *state,
+			  struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_get - get input pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_state *state,
+			 struct netlink_ext_ack *extack)
+{
+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_input_prio_get - get dpll's input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: on success - returns input priority on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			u32 *prio, struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	*prio = d->input_prio[p->idx];
+	ice_dpll_cb_unlock(pf);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_input_prio_set - set dpll input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: input priority to be set on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
+			const struct dpll_device *dpll, void *dpll_priv,
+			u32 prio, struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_dpll *d = dpll_priv;
+	struct ice_pf *pf = d->pf;
+	int ret;
+
+	if (prio > ICE_DPLL_PRIO_MAX) {
+		NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
+				   ICE_DPLL_PRIO_MAX);
+		return -EINVAL;
+	}
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack);
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_input_direction - callback for get input pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds input pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a input pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
+			 const struct dpll_device *dpll, void *dpll_priv,
+			 enum dpll_pin_direction *direction,
+			 struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_INPUT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_output_direction - callback for get output pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds output pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of an output pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
+			  const struct dpll_device *dpll, void *dpll_priv,
+			  enum dpll_pin_direction *direction,
+			  struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_OUTPUT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: parent private data pointer passed on pin registration
+ * @state: state to be set on pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
+			       const struct dpll_pin *parent_pin,
+			       void *parent_pin_priv,
+			       enum dpll_pin_state state,
+			       struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
+	struct ice_pf *pf = p->pf;
+	u32 hw_idx;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+	if (hw_idx >= pf->dplls.num_inputs)
+		goto unlock;
+
+	if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
+	    (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "pin:%u state:%u on parent:%u already set",
+				   p->idx, state, parent->idx);
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
+					 &p->freq);
+	if (ret)
+		NL_SET_ERR_MSG_FMT(extack,
+				   "err:%d %s failed to set pin state:%u for pin:%u on parent:%u\n",
+				   ret,
+				   ice_aq_str(pf->hw.adminq.sq_last_status),
+				   state, p->idx, parent->idx);
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: pin parent priv data pointer passed on pin registration
+ * @state: on success holds pin state on parent pin
+ * @extack: error reporting
+ *
+ * dpll subsystem callback, get a state of a recovered clock pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
+			       const struct dpll_pin *parent_pin,
+			       void *parent_pin_priv,
+			       enum dpll_pin_state *state,
+			       struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+	struct ice_pf *pf = p->pf;
+	u32 hw_idx;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, extack);
+	if (ret)
+		return ret;
+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+	if (hw_idx >= pf->dplls.num_inputs)
+		goto unlock;
+
+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+					extack);
+	if (ret)
+		goto unlock;
+
+	*state = p->state[hw_idx];
+	ret = 0;
+unlock:
+	ice_dpll_cb_unlock(pf);
+
+	return ret;
+}
+
+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
+	.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
+	.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
+	.direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_input_ops = {
+	.frequency_get = ice_dpll_input_frequency_get,
+	.frequency_set = ice_dpll_input_frequency_set,
+	.state_on_dpll_get = ice_dpll_input_state_get,
+	.state_on_dpll_set = ice_dpll_input_state_set,
+	.prio_get = ice_dpll_input_prio_get,
+	.prio_set = ice_dpll_input_prio_set,
+	.direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_output_ops = {
+	.frequency_get = ice_dpll_output_frequency_get,
+	.frequency_set = ice_dpll_output_frequency_set,
+	.state_on_dpll_get = ice_dpll_output_state_get,
+	.state_on_dpll_set = ice_dpll_output_state_set,
+	.direction_get = ice_dpll_output_direction,
+};
+
+static const struct dpll_device_ops ice_dpll_ops = {
+	.lock_status_get = ice_dpll_lock_status_get,
+	.mode_supported = ice_dpll_mode_supported,
+	.mode_get = ice_dpll_mode_get,
+	.mode_set = ice_dpll_mode_set,
+};
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ *
+ * Return: generated clock id for the board
+ */
+static u64 ice_generate_clock_id(struct ice_pf *pf)
+{
+	return pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_notify_changes - notify dpll subsystem about changes
+ * @d: pointer do dpll
+ *
+ * Once change detected appropriate event is submitted to the dpll subsystem.
+ */
+static void ice_dpll_notify_changes(struct ice_dpll *d)
+{
+	if (d->prev_dpll_state != d->dpll_state) {
+		d->prev_dpll_state = d->dpll_state;
+		dpll_device_change_ntf(d->dpll);
+	}
+	if (d->prev_input != d->active_input) {
+		if (d->prev_input)
+			dpll_pin_change_ntf(d->prev_input);
+		d->prev_input = d->active_input;
+		if (d->active_input)
+			dpll_pin_change_ntf(d->active_input);
+	}
+}
+
+/**
+ * ice_dpll_update_state - update dpll state
+ * @pf: pf private structure
+ * @d: pointer to queried dpll device
+ * @init: if function called on initialization of ice dpll
+ *
+ * Poll current state of dpll from hw and update ice_dpll struct.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - AQ failure
+ */
+static int
+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
+{
+	struct ice_dpll_pin *p = NULL;
+	int ret;
+
+	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
+				&d->input_idx, &d->ref_state, &d->eec_mode,
+				&d->phase_shift, &d->dpll_state, &d->mode);
+
+	dev_dbg(ice_pf_to_dev(pf),
+		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
+		d->dpll_idx, d->prev_input_idx, d->input_idx,
+		d->dpll_state, d->prev_dpll_state, d->mode);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(pf->hw.adminq.sq_last_status));
+		return ret;
+	}
+	if (init) {
+		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
+		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
+			d->active_input = pf->dplls.inputs[d->input_idx].pin;
+		p = &pf->dplls.inputs[d->input_idx];
+		return ice_dpll_pin_state_update(pf, p,
+						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
+	}
+	if (d->dpll_state == DPLL_LOCK_STATUS_HOLDOVER ||
+	    d->dpll_state == DPLL_LOCK_STATUS_UNLOCKED) {
+		d->active_input = NULL;
+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID)
+			p = &pf->dplls.inputs[d->input_idx];
+		d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
+		d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
+		if (!p)
+			return 0;
+		ret = ice_dpll_pin_state_update(pf, p,
+						ICE_DPLL_PIN_TYPE_INPUT, NULL);
+	} else if (d->input_idx != d->prev_input_idx) {
+		if (d->prev_input_idx != ICE_DPLL_PIN_IDX_INVALID) {
+			p = &pf->dplls.inputs[d->prev_input_idx];
+			ice_dpll_pin_state_update(pf, p,
+						  ICE_DPLL_PIN_TYPE_INPUT,
+						  NULL);
+		}
+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID) {
+			p = &pf->dplls.inputs[d->input_idx];
+			d->active_input = p->pin;
+			ice_dpll_pin_state_update(pf, p,
+						  ICE_DPLL_PIN_TYPE_INPUT,
+						  NULL);
+		}
+		d->prev_input_idx = d->input_idx;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ * Context: Holds pf->dplls.lock
+ */
+static void ice_dpll_periodic_work(struct kthread_work *work)
+{
+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	int ret;
+
+	ret = ice_dpll_cb_lock(pf, NULL);
+	if (ret == -EBUSY)
+		goto resched;
+	else if (ret)
+		return;
+	ret = ice_dpll_update_state(pf, de, false);
+	if (!ret)
+		ret = ice_dpll_update_state(pf, dp, false);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
+			dev_err(ice_pf_to_dev(pf),
+				"EEC/PPS DPLLs periodic work disabled\n");
+			return;
+		}
+	}
+	ice_dpll_cb_unlock(pf);
+	ice_dpll_notify_changes(de);
+	ice_dpll_notify_changes(dp);
+
+resched:
+	/* Run twice a second or reschedule if update failed */
+	kthread_queue_delayed_work(d->kworker, &d->work,
+				   ret ? msecs_to_jiffies(10) :
+				   msecs_to_jiffies(500));
+}
+
+/**
+ * ice_dpll_release_pins - release pins resources from dpll subsystem
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Release resources of given pins array in the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		dpll_pin_put(pins[i].pin);
+}
+
+/**
+ * ice_dpll_get_pins - get pins from dpll subsystem
+ * @pf: board private structure
+ * @pins: pointer to pins array
+ * @start_idx: get starts from this pin idx value
+ * @count: number of pins
+ * @clock_id: clock_id of dpll device
+ *
+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
+ * pins array.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - allocation failure reason
+ */
+static int
+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
+		  int start_idx, int count, u64 clock_id)
+{
+	int i, ret;
+
+	for (i = 0; i < count; i++) {
+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
+					   &pins[i].prop);
+		if (IS_ERR(pins[i].pin)) {
+			ret = PTR_ERR(pins[i].pin);
+			goto release_pins;
+		}
+	}
+
+	return 0;
+
+release_pins:
+	while (--i >= 0)
+		dpll_pin_put(pins[i].pin);
+	return ret;
+}
+
+/**
+ * ice_dpll_unregister_pins - unregister pins from a dpll
+ * @dpll: dpll device pointer
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ *
+ * Unregister pins of a given array of pins from given dpll device registered in
+ * dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+			 const struct dpll_pin_ops *ops, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @dpll: dpll pointer to register pins with
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ *
+ * Register pins of a given array with given dpll in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+		       const struct dpll_pin_ops *ops, int count)
+{
+	int ret, i;
+
+	for (i = 0; i < count; i++) {
+		ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
+		if (ret)
+			goto unregister_pins;
+	}
+
+	return 0;
+
+unregister_pins:
+	while (--i >= 0)
+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Context: Called under pf->dplls.lock
+ * If cgu is owned unregister pins from given dplls.
+ * Release pins resources to the dpll subsystem.
+ */
+static void
+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
+			    const struct dpll_pin_ops *ops,
+			    struct dpll_device *first,
+			    struct dpll_device *second)
+{
+	if (cgu) {
+		ice_dpll_unregister_pins(first, pins, ops, count);
+		ice_dpll_unregister_pins(second, pins, ops, count);
+	}
+	ice_dpll_release_pins(pins, count);
+}
+
+/**
+ * ice_dpll_init_direct_pins - initialize direct pins
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Allocate directly connected pins of a given array in dpll subsystem.
+ * If cgu is owned register allocated pins with given dplls.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
+			  struct ice_dpll_pin *pins, int start_idx, int count,
+			  const struct dpll_pin_ops *ops,
+			  struct dpll_device *first, struct dpll_device *second)
+{
+	int ret;
+
+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
+	if (ret)
+		return ret;
+	if (cgu) {
+		ret = ice_dpll_register_pins(first, pins, ops, count);
+		if (ret)
+			goto release_pins;
+		ret = ice_dpll_register_pins(second, pins, ops, count);
+		if (ret)
+			goto unregister_first;
+	}
+
+	return 0;
+
+unregister_first:
+	ice_dpll_unregister_pins(first, pins, ops, count);
+release_pins:
+	ice_dpll_release_pins(pins, count);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
+ * @pf: board private structure
+ *
+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
+	struct dpll_pin *parent;
+	int i;
+
+	for (i = 0; i < rclk->num_parents; i++) {
+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
+		if (!parent)
+			continue;
+		dpll_pin_on_pin_unregister(parent, rclk->pin,
+					   &ice_dpll_rclk_ops, rclk);
+	}
+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
+		return;
+	netdev_dpll_pin_clear(vsi->netdev);
+	dpll_pin_put(rclk->pin);
+}
+
+/**
+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
+ * @pf: board private structure
+ * @pin: pin to register
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @ops: callback ops registered with the pins
+ *
+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
+ * pin with the parents it has in the info. Register pin with the pf's main vsi
+ * netdev.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			int start_idx, const struct dpll_pin_ops *ops)
+{
+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
+	struct dpll_pin *parent;
+	int ret, i;
+
+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
+				pf->dplls.clock_id);
+	if (ret)
+		return ret;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
+		if (!parent) {
+			ret = -ENODEV;
+			goto unregister_pins;
+		}
+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
+					       ops, &pf->dplls.rclk);
+		if (ret)
+			goto unregister_pins;
+	}
+	if (WARN_ON((!vsi || !vsi->netdev)))
+		return -EINVAL;
+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
+
+	return 0;
+
+unregister_pins:
+	while (i) {
+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
+	}
+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_pins - deinitialize direct pins
+ * @pf: board private structure
+ * @cgu: if cgu is controlled by this pf
+ *
+ * If cgu is owned unregister directly connected pins from the dplls.
+ * Release resources of directly connected pins from the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
+{
+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
+	int num_outputs = pf->dplls.num_outputs;
+	int num_inputs = pf->dplls.num_inputs;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+
+	ice_dpll_deinit_rclk_pin(pf);
+	if (cgu) {
+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
+					 num_inputs);
+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
+					 num_inputs);
+	}
+	ice_dpll_release_pins(inputs, num_inputs);
+	if (cgu) {
+		ice_dpll_unregister_pins(dp->dpll, outputs,
+					 &ice_dpll_output_ops, num_outputs);
+		ice_dpll_unregister_pins(de->dpll, outputs,
+					 &ice_dpll_output_ops, num_outputs);
+		ice_dpll_release_pins(outputs, num_outputs);
+	}
+}
+
+/**
+ * ice_dpll_init_pins - init pins and register pins with a dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
+ * subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
+{
+	u32 rclk_idx;
+	int ret;
+
+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
+					pf->dplls.num_inputs,
+					&ice_dpll_input_ops,
+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+	if (ret)
+		return ret;
+	if (cgu) {
+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
+						pf->dplls.num_inputs,
+						pf->dplls.num_outputs,
+						&ice_dpll_output_ops,
+						pf->dplls.eec.dpll,
+						pf->dplls.pps.dpll);
+		if (ret)
+			goto deinit_inputs;
+	}
+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
+				      &ice_dpll_rclk_ops);
+	if (ret)
+		goto deinit_outputs;
+
+	return 0;
+deinit_outputs:
+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
+				    pf->dplls.num_outputs,
+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
+deinit_inputs:
+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit_dpll - deinitialize dpll device
+ * @pf: board private structure
+ * @d: pointer to ice_dpll
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * If cgu is owned unregister the dpll from dpll subsystem.
+ * Release resources of dpll device from dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
+{
+	if (cgu)
+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
+	dpll_device_put(d->dpll);
+}
+
+/**
+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
+ * @pf: board private structure
+ * @d: dpll to be initialized
+ * @cgu: if cgu is present and controlled by this NIC
+ * @type: type of dpll being initialized
+ *
+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
+ * by this NIC, register dpll with the callback ops.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int
+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
+		   enum dpll_type type)
+{
+	u64 clock_id = pf->dplls.clock_id;
+	int ret;
+
+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
+	if (IS_ERR(d->dpll)) {
+		ret = PTR_ERR(d->dpll);
+		dev_err(ice_pf_to_dev(pf),
+			"dpll_device_get failed (%p) err=%d\n", d, ret);
+		return ret;
+	}
+	d->pf = pf;
+	if (cgu) {
+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
+		if (ret) {
+			dpll_device_put(d->dpll);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_deinit_worker - deinitialize dpll kworker
+ * @pf: board private structure
+ *
+ * Stop dpll's kworker, release it's resources.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+
+	kthread_cancel_delayed_work_sync(&d->work);
+	kthread_destroy_worker(d->kworker);
+}
+
+/**
+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
+ * @pf: board private structure
+ *
+ * Create and start DPLLs periodic worker.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - create worker failure
+ */
+static int ice_dpll_init_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct kthread_worker *kworker;
+
+	ice_dpll_update_state(pf, &d->eec, true);
+	ice_dpll_update_state(pf, &d->pps, true);
+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
+	kworker = kthread_create_worker(0, "ice-dplls-%s",
+					dev_name(ice_pf_to_dev(pf)));
+	if (IS_ERR(kworker))
+		return PTR_ERR(kworker);
+	d->kworker = kworker;
+	d->cgu_state_acq_err_num = 0;
+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_init_info_direct_pins - initializes direct pins info
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information for directly connected pins, cache them in pf's pins
+ * structures.
+ *
+ * Context: Called under pf->dplls.lock.
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
+			       enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
+	struct ice_hw *hw = &pf->hw;
+	struct ice_dpll_pin *pins;
+	int num_pins, i, ret;
+	u8 freq_supp_num;
+	bool input;
+
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+		input = true;
+		break;
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+		input = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
+		if (input) {
+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
+						      &de->input_prio[i]);
+			if (ret)
+				return ret;
+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
+						      &dp->input_prio[i]);
+			if (ret)
+				return ret;
+			pins[i].prop.capabilities |=
+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
+		}
+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
+		if (ret)
+			return ret;
+		pins[i].prop.freq_supported =
+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
+		pins[i].prop.freq_supported_num = freq_supp_num;
+		pins[i].pf = pf;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
+ * @pf: board private structure
+ *
+ * Init information for rclk pin, cache them in pf->dplls.rclk.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
+
+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+	pin->pf = pf;
+
+	return ice_dpll_pin_state_update(pf, pin,
+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
+}
+
+/**
+ * ice_dpll_init_pins_info - init pins info wrapper
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Wraps functions for pin initialization.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
+{
+	switch (pin_type) {
+	case ICE_DPLL_PIN_TYPE_INPUT:
+	case ICE_DPLL_PIN_TYPE_OUTPUT:
+		return ice_dpll_init_info_direct_pins(pf, pin_type);
+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+		return ice_dpll_init_info_rclk_pin(pf);
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * ice_dpll_deinit_info - release memory allocated for pins info
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_info(struct ice_pf *pf)
+{
+	kfree(pf->dplls.inputs);
+	kfree(pf->dplls.outputs);
+	kfree(pf->dplls.eec.input_prio);
+	kfree(pf->dplls.pps.input_prio);
+}
+
+/**
+ * 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).
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
+{
+	struct ice_aqc_get_cgu_abilities abilities;
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_hw *hw = &pf->hw;
+	int ret, alloc_size, i;
+
+	d->clock_id = ice_generate_clock_id(pf);
+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"err:%d %s failed to read cgu abilities\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status));
+		return ret;
+	}
+
+	de->dpll_idx = abilities.eec_dpll_idx;
+	dp->dpll_idx = abilities.pps_dpll_idx;
+	d->num_inputs = abilities.num_inputs;
+	d->num_outputs = abilities.num_outputs;
+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
+
+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->inputs)
+		return -ENOMEM;
+
+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!de->input_prio)
+		return -ENOMEM;
+
+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!dp->input_prio)
+		return -ENOMEM;
+
+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
+	if (ret)
+		goto deinit_info;
+
+	if (cgu) {
+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+		if (!d->outputs)
+			goto deinit_info;
+
+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+		if (ret)
+			goto deinit_info;
+	}
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
+					&pf->dplls.rclk.num_parents);
+	if (ret)
+		return ret;
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+	if (ret)
+		return ret;
+	de->mode = DPLL_MODE_AUTOMATIC;
+	dp->mode = DPLL_MODE_AUTOMATIC;
+
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
+
+	return 0;
+
+deinit_info:
+	dev_err(ice_pf_to_dev(pf),
+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
+		__func__, d->inputs, de->input_prio,
+		dp->input_prio, d->outputs);
+	ice_dpll_deinit_info(pf);
+	return ret;
+}
+
+/**
+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * Handles the cleanup work required after dpll initialization,freeing resources
+ * and unregistering the dpll, pin and all resources used for handling them.
+ *
+ * Context: Function holds pf->dplls.lock mutex.
+ */
+void ice_dpll_deinit(struct ice_pf *pf)
+{
+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
+
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+		return;
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+
+	ice_dpll_deinit_pins(pf, cgu);
+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+	ice_dpll_deinit_info(pf);
+	if (cgu)
+		ice_dpll_deinit_worker(pf);
+	mutex_destroy(&pf->dplls.lock);
+}
+
+/**
+ * ice_dpll_init - initialize support for dpll subsystem
+ * @pf: board private structure
+ *
+ * Set up the device dplls, register them and pins connected within Linux dpll
+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
+ * configuration requests.
+ *
+ * Context: Function initializes and holds pf->dplls.lock mutex.
+ */
+void ice_dpll_init(struct ice_pf *pf)
+{
+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
+	struct ice_dplls *d = &pf->dplls;
+	int err = 0;
+
+	err = ice_dpll_init_info(pf, cgu);
+	if (err)
+		goto err_exit;
+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
+	if (err)
+		goto deinit_info;
+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
+	if (err)
+		goto deinit_eec;
+	err = ice_dpll_init_pins(pf, cgu);
+	if (err)
+		goto deinit_pps;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	if (cgu) {
+		err = ice_dpll_init_worker(pf);
+		if (err)
+			goto deinit_pins;
+	}
+
+	return;
+
+deinit_pins:
+	ice_dpll_deinit_pins(pf, cgu);
+deinit_pps:
+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+deinit_eec:
+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+deinit_info:
+	ice_dpll_deinit_info(pf);
+err_exit:
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+	mutex_unlock(&d->lock);
+	mutex_destroy(&d->lock);
+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022, Intel Corporation. */
+
+#ifndef _ICE_DPLL_H_
+#define _ICE_DPLL_H_
+
+#include "ice.h"
+
+#define ICE_DPLL_PRIO_MAX	0xF
+#define ICE_DPLL_RCLK_NUM_MAX	4
+
+/** ice_dpll_pin - store info about pins
+ * @pin: dpll pin structure
+ * @pf: pointer to pf, which has registered the dpll_pin
+ * @idx: ice pin private idx
+ * @num_parents: hols number of parent pins
+ * @parent_idx: hold indexes of parent pins
+ * @flags: pin flags returned from HW
+ * @state: state of a pin
+ * @prop: pin properities
+ * @freq: current frequency of a pin
+ */
+struct ice_dpll_pin {
+	struct dpll_pin *pin;
+	struct ice_pf *pf;
+	u8 idx;
+	u8 num_parents;
+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
+	struct dpll_pin_properties prop;
+	u32 freq;
+};
+
+/** ice_dpll - store info required for DPLL control
+ * @dpll: pointer to dpll dev
+ * @pf: pointer to pf, which has registered the dpll_device
+ * @dpll_idx: index of dpll on the NIC
+ * @input_idx: currently selected input index
+ * @prev_input_idx: previously selected input index
+ * @ref_state: state of dpll reference signals
+ * @eec_mode: eec_mode dpll is configured for
+ * @phase_shift: phase shift delay of a dpll
+ * @input_prio: priorities of each input
+ * @dpll_state: current dpll sync state
+ * @prev_dpll_state: last dpll sync state
+ * @active_input: pointer to active input pin
+ * @prev_input: pointer to previous active input pin
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	struct ice_pf *pf;
+	u8 dpll_idx;
+	u8 input_idx;
+	u8 prev_input_idx;
+	u8 ref_state;
+	u8 eec_mode;
+	s64 phase_shift;
+	u8 *input_prio;
+	enum dpll_lock_status dpll_state;
+	enum dpll_lock_status prev_dpll_state;
+	enum dpll_mode mode;
+	struct dpll_pin *active_input;
+	struct dpll_pin *prev_input;
+};
+
+/** ice_dplls - store info required for CCU (clock controlling unit)
+ * @kworker: periodic worker
+ * @work: periodic work
+ * @lock: locks access to configuration of a dpll
+ * @eec: pointer to EEC dpll dev
+ * @pps: pointer to PPS dpll dev
+ * @inputs: input pins pointer
+ * @outputs: output pins pointer
+ * @rclk: recovered pins pointer
+ * @num_inputs: number of input pins available on dpll
+ * @num_outputs: number of output pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ * @base_rclk_idx: idx of first pin used for clock revocery pins
+ * @clock_id: clock_id of dplls
+ */
+struct ice_dplls {
+	struct kthread_worker *kworker;
+	struct kthread_delayed_work work;
+	struct mutex lock;
+	struct ice_dpll eec;
+	struct ice_dpll pps;
+	struct ice_dpll_pin *inputs;
+	struct ice_dpll_pin *outputs;
+	struct ice_dpll_pin rclk;
+	u8 num_inputs;
+	u8 num_outputs;
+	int cgu_state_acq_err_num;
+	u8 base_rclk_idx;
+	u64 clock_id;
+	s32 input_phase_adj_max;
+	s32 output_phase_adj_max;
+};
+
+void ice_dpll_init(struct ice_pf *pf);
+
+void ice_dpll_deinit(struct ice_pf *pf);
+
+#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 19a5e7f3a075..0a94daaf3d20 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4613,6 +4613,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");
@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
 		ice_gnss_exit(pf);
 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
 		ice_ptp_release(pf);
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
+	    ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_deinit(pf);
 }
 
 static void ice_init_wakeup(struct ice_pf *pf)
-- 
2.27.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] 109+ messages in thread

* [PATCH 10/11] ptp_ocp: implement DPLL ops
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:19   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- implement monitoring thread and netlink notification
- implement state on dpll callback

 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 366 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 307 insertions(+), 60 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 32dff1b4f891..e4da62ac9a9f 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 20a974ced8d6..7f06ae6e2dd8 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -260,12 +261,21 @@ enum ptp_ocp_sma_mode {
 	SMA_MODE_OUT,
 };
 
+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+	DPLL_PIN_FREQUENCY_10MHZ,
+	DPLL_PIN_FREQUENCY_IRIG_B,
+	DPLL_PIN_FREQUENCY_DCF77,
+};
+
 struct ptp_ocp_sma_connector {
 	enum	ptp_ocp_sma_mode mode;
 	bool	fixed_fcn;
 	bool	fixed_dir;
 	bool	disabled;
 	u8	default_fcn;
+	struct dpll_pin		   *dpll_pin;
+	struct dpll_pin_properties dpll_prop;
 };
 
 struct ocp_attr_group {
@@ -294,6 +304,7 @@ struct ptp_ocp_serial_port {
 
 #define OCP_BOARD_ID_LEN		13
 #define OCP_SERIAL_LEN			6
+#define OCP_SMA_NUM			4
 
 struct ptp_ocp {
 	struct pci_dev		*pdev;
@@ -331,7 +342,9 @@ struct ptp_ocp {
 	const struct attribute_group **attr_group;
 	const struct ptp_ocp_eeprom_map *eeprom_map;
 	struct dentry		*debug_root;
+	bool			sync;
 	time64_t		gnss_lost;
+	struct delayed_work	sync_work;
 	int			id;
 	int			n_irqs;
 	struct ptp_ocp_serial_port	gnss_port;
@@ -350,8 +363,9 @@ struct ptp_ocp {
 	u32			ts_window_adjust;
 	u64			fw_cap;
 	struct ptp_ocp_signal	signal[4];
-	struct ptp_ocp_sma_connector sma[4];
+	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -835,6 +849,7 @@ static DEFINE_IDR(ptp_ocp_idr);
 struct ocp_selector {
 	const char *name;
 	int value;
+	u64 frequency;
 };
 
 static const struct ocp_selector ptp_ocp_clock[] = {
@@ -855,31 +870,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
 #define SMA_SELECT_MASK		GENMASK(14, 0)
 
 static const struct ocp_selector ptp_ocp_sma_in[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "PPS2",	.value = 0x0002 },
-	{ .name = "TS1",	.value = 0x0004 },
-	{ .name = "TS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
-	{ .name = "TS3",	.value = 0x0040 },
-	{ .name = "TS4",	.value = 0x0080 },
-	{ .name = "FREQ1",	.value = 0x0100 },
-	{ .name = "FREQ2",	.value = 0x0200 },
-	{ .name = "FREQ3",	.value = 0x0400 },
-	{ .name = "FREQ4",	.value = 0x0800 },
-	{ .name = "None",	.value = SMA_DISABLE },
+	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
+	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
+	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
+	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
+	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
+	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
+	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
+	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
+	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
+	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
+	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
+	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
+	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
+	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_sma_out[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PHC",	.value = 0x0001 },
-	{ .name = "MAC",	.value = 0x0002 },
-	{ .name = "GNSS1",	.value = 0x0004 },
-	{ .name = "GNSS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
+	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
+	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
+	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
+	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
 	{ .name = "GEN1",	.value = 0x0040 },
 	{ .name = "GEN2",	.value = 0x0080 },
 	{ .name = "GEN3",	.value = 0x0100 },
@@ -890,15 +905,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_in[] = {
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "10Mhz",	.value = 0x0008 },
+	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_out[] = {
-	{ .name = "PHC",	.value = 0x0002 },
-	{ .name = "GNSS",	.value = 0x0004 },
-	{ .name = "10Mhz",	.value = 0x0010 },
+	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
 	{ }
 };
 
@@ -1351,7 +1366,6 @@ static int
 ptp_ocp_init_clock(struct ptp_ocp *bp)
 {
 	struct timespec64 ts;
-	bool sync;
 	u32 ctrl;
 
 	ctrl = OCP_CTRL_ENABLE;
@@ -1375,8 +1389,8 @@ ptp_ocp_init_clock(struct ptp_ocp *bp)
 
 	ptp_ocp_estimate_pci_timing(bp);
 
-	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
-	if (!sync) {
+	bp->sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+	if (!bp->sync) {
 		ktime_get_clocktai_ts64(&ts);
 		ptp_ocp_settime(&bp->ptp_info, &ts);
 	}
@@ -2289,22 +2303,35 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
 static void
 ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.board_label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
 	/* defaults */
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].default_fcn = i & 1;
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.board_label =
+			bp->ptp_info.pin_config[i].name;
+	}
 	bp->sma[0].mode = SMA_MODE_IN;
 	bp->sma[1].mode = SMA_MODE_IN;
 	bp->sma[2].mode = SMA_MODE_OUT;
 	bp->sma[3].mode = SMA_MODE_OUT;
-	for (i = 0; i < 4; i++)
-		bp->sma[i].default_fcn = i & 1;
-
 	/* If no SMA1 map, the pin functions and directions are fixed. */
 	if (!bp->sma_map1) {
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < OCP_SMA_NUM; i++) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			bp->sma[1].dpll_prop.capabilities &=
+				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 		}
 		return;
 	}
@@ -2314,7 +2341,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 	 */
 	reg = ioread32(&bp->sma_map2->gpio2);
 	if (reg == 0xffffffff) {
-		for (i = 0; i < 4; i++)
+		for (i = 0; i < OCP_SMA_NUM; i++)
 			bp->sma[i].fixed_dir = true;
 	} else {
 		reg = ioread32(&bp->sma_map1->gpio1);
@@ -2336,7 +2363,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
 };
 
 static int
-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
+ptp_ocp_set_pins(struct ptp_ocp *bp)
 {
 	struct ptp_pin_desc *config;
 	int i;
@@ -2403,16 +2430,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 
 	ptp_ocp_tod_init(bp);
 	ptp_ocp_nmea_out_init(bp);
-	ptp_ocp_sma_init(bp);
 	ptp_ocp_signal_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
 	if (err)
 		return err;
 
-	err = ptp_ocp_fb_set_pins(bp);
+	err = ptp_ocp_set_pins(bp);
 	if (err)
 		return err;
+	ptp_ocp_sma_init(bp);
 
 	return ptp_ocp_init_clock(bp);
 }
@@ -2452,6 +2479,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
 static void
 ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.board_label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = 0,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
@@ -2466,16 +2501,17 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
 
-	/* If no SMA map, the pin functions and directions are fixed. */
-	if (!bp->art_sma) {
-		for (i = 0; i < 4; i++) {
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		/* If no SMA map, the pin functions and directions are fixed. */
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.board_label =
+			bp->ptp_info.pin_config[i].name;
+		if (!bp->art_sma) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			continue;
 		}
-		return;
-	}
-
-	for (i = 0; i < 4; i++) {
 		reg = ioread32(&bp->art_sma->map[i].gpio);
 
 		switch (reg & 0xff) {
@@ -2486,9 +2522,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 		case 1:
 		case 8:
 			bp->sma[i].mode = SMA_MODE_IN;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		default:
 			bp->sma[i].mode = SMA_MODE_OUT;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		}
 	}
@@ -2555,6 +2595,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 	/* Enable MAC serial port during initialisation */
 	iowrite32(1, &bp->board_config->mro50_serial_activate);
 
+	err = ptp_ocp_set_pins(bp);
+	if (err)
+		return err;
 	ptp_ocp_sma_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
@@ -2696,16 +2739,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;
@@ -2740,6 +2776,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
 	return val;
 }
 
+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+	enum ptp_ocp_sma_mode mode;
+	int val;
+
+	mode = sma->mode;
+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+	if (val < 0)
+		return val;
+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
 static ssize_t
 sma1_store(struct device *dev, struct device_attribute *attr,
 	   const char *buf, size_t count)
@@ -3834,9 +3884,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
 		strcpy(buf, "unknown");
 		break;
 	}
-	val = ioread32(&bp->reg->status);
 	seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf,
-		   val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced");
+		   bp->sync ? "sync" : "unsynced");
 
 	if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) {
 		struct timespec64 sys_ts;
@@ -4068,7 +4117,6 @@ ptp_ocp_phc_info(struct ptp_ocp *bp)
 {
 	struct timespec64 ts;
 	u32 version, select;
-	bool sync;
 
 	version = ioread32(&bp->reg->version);
 	select = ioread32(&bp->reg->select);
@@ -4077,11 +4125,10 @@ ptp_ocp_phc_info(struct ptp_ocp *bp)
 		 ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16),
 		 ptp_clock_index(bp->ptp));
 
-	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
 	if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL))
 		dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n",
 			 ts.tv_sec, ts.tv_nsec,
-			 sync ? "in-sync" : "UNSYNCED");
+			 bp->sync ? "in-sync" : "UNSYNCED");
 }
 
 static void
@@ -4178,12 +4225,168 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+					void *priv,
+					enum dpll_lock_status *status,
+					struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+
+	*status = bp->sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
+
+	return 0;
+}
+
+static int ptp_ocp_dpll_state_get(const struct dpll_pin *pin, void *pin_priv,
+				  const struct dpll_device *dpll, void *priv,
+				  enum dpll_pin_state *state,
+				  struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+	int idx;
+
+	if (bp->pps_select) {
+		idx = ioread32(&bp->pps_select->gpio1);
+		*state = (&bp->sma[idx] == pin_priv) ? DPLL_PIN_STATE_CONNECTED :
+						      DPLL_PIN_STATE_SELECTABLE;
+		return 0;
+	}
+	NL_SET_ERR_MSG(extack, "pin selection is not supported on current HW");
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
+				 u32 *mode, struct netlink_ext_ack *extack)
+{
+	*mode = DPLL_MODE_AUTOMATIC;
+	return 0;
+}
+
+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
+					void *priv, const enum dpll_mode mode,
+					struct netlink_ext_ack *extack)
+{
+	return mode == DPLL_MODE_AUTOMATIC;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *priv,
+				      enum dpll_pin_direction *direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+
+	*direction = sma->mode == SMA_MODE_IN ?
+				  DPLL_PIN_DIRECTION_INPUT :
+				  DPLL_PIN_DIRECTION_OUTPUT;
+	return 0;
+}
+
+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv,
+				      enum dpll_pin_direction direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	enum ptp_ocp_sma_mode mode;
+	int sma_nr = (sma - bp->sma);
+
+	if (sma->fixed_dir)
+		return -EOPNOTSUPP;
+	mode = direction == DPLL_PIN_DIRECTION_INPUT ?
+			    SMA_MODE_IN : SMA_MODE_OUT;
+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	int val, i;
+
+	if (sma->fixed_fcn)
+		return -EOPNOTSUPP;
+
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (tbl[i].frequency == frequency)
+			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 *frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	u32 val;
+	int i;
+
+	val = bp->sma_op->get(bp, sma_nr);
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (val == tbl[i].value) {
+			*frequency = tbl[i].frequency;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static const struct dpll_device_ops dpll_ops = {
+	.lock_status_get = ptp_ocp_dpll_lock_status_get,
+	.mode_get = ptp_ocp_dpll_mode_get,
+	.mode_supported = ptp_ocp_dpll_mode_supported,
+};
+
+static const struct dpll_pin_ops dpll_pins_ops = {
+	.frequency_get = ptp_ocp_dpll_frequency_get,
+	.frequency_set = ptp_ocp_dpll_frequency_set,
+	.direction_get = ptp_ocp_dpll_direction_get,
+	.direction_set = ptp_ocp_dpll_direction_set,
+	.state_on_dpll_get = ptp_ocp_dpll_state_get,
+};
+
+static void
+ptp_ocp_sync_work(struct work_struct *work)
+{
+	struct ptp_ocp *bp;
+	bool sync;
+
+	bp = container_of(work, struct ptp_ocp, sync_work.work);
+	sync = !!(ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC);
+
+	if (bp->sync != sync)
+		dpll_device_change_ntf(bp->dpll);
+
+	bp->sync = sync;
+
+	queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ);
+}
+
 static int
 ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct devlink *devlink;
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
+	u64 clkid;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4202,6 +4405,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		goto out_disable;
 
+	INIT_DELAYED_WORK(&bp->sync_work, ptp_ocp_sync_work);
+
 	/* compat mode.
 	 * Older FPGA firmware only returns 2 irq's.
 	 * allow this - if not all of the IRQ's are returned, skip the
@@ -4233,8 +4438,40 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	ptp_ocp_info(bp);
 	devlink_register(devlink);
-	return 0;
 
+	clkid = pci_get_dsn(pdev);
+	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
+	if (IS_ERR(bp->dpll)) {
+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
+		goto out;
+	}
+
+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
+	if (err)
+		goto out;
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
+		if (IS_ERR(bp->sma[i].dpll_pin))
+			goto out_dpll;
+
+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
+					&bp->sma[i]);
+		if (err) {
+			dpll_pin_put(bp->sma[i].dpll_pin);
+			goto out_dpll;
+		}
+	}
+	queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ);
+
+	return 0;
+out_dpll:
+	while (i) {
+		--i;
+		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
+		dpll_pin_put(bp->sma[i].dpll_pin);
+	}
+	dpll_device_put(bp->dpll);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4249,7 +4486,17 @@ ptp_ocp_remove(struct pci_dev *pdev)
 {
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
+	int i;
 
+	cancel_delayed_work_sync(&bp->sync_work);
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		if (bp->sma[i].dpll_pin) {
+			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
+			dpll_pin_put(bp->sma[i].dpll_pin);
+		}
+	}
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
+	dpll_device_put(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
 	pci_disable_device(pdev);
-- 
2.27.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] 109+ messages in thread

* [PATCH 10/11] ptp_ocp: implement DPLL ops
@ 2023-07-20  9:19   ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

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>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
RFC v9->v0:
- implement monitoring thread and netlink notification
- implement state on dpll callback

 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 366 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 307 insertions(+), 60 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 32dff1b4f891..e4da62ac9a9f 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 20a974ced8d6..7f06ae6e2dd8 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -260,12 +261,21 @@ enum ptp_ocp_sma_mode {
 	SMA_MODE_OUT,
 };
 
+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+	DPLL_PIN_FREQUENCY_10MHZ,
+	DPLL_PIN_FREQUENCY_IRIG_B,
+	DPLL_PIN_FREQUENCY_DCF77,
+};
+
 struct ptp_ocp_sma_connector {
 	enum	ptp_ocp_sma_mode mode;
 	bool	fixed_fcn;
 	bool	fixed_dir;
 	bool	disabled;
 	u8	default_fcn;
+	struct dpll_pin		   *dpll_pin;
+	struct dpll_pin_properties dpll_prop;
 };
 
 struct ocp_attr_group {
@@ -294,6 +304,7 @@ struct ptp_ocp_serial_port {
 
 #define OCP_BOARD_ID_LEN		13
 #define OCP_SERIAL_LEN			6
+#define OCP_SMA_NUM			4
 
 struct ptp_ocp {
 	struct pci_dev		*pdev;
@@ -331,7 +342,9 @@ struct ptp_ocp {
 	const struct attribute_group **attr_group;
 	const struct ptp_ocp_eeprom_map *eeprom_map;
 	struct dentry		*debug_root;
+	bool			sync;
 	time64_t		gnss_lost;
+	struct delayed_work	sync_work;
 	int			id;
 	int			n_irqs;
 	struct ptp_ocp_serial_port	gnss_port;
@@ -350,8 +363,9 @@ struct ptp_ocp {
 	u32			ts_window_adjust;
 	u64			fw_cap;
 	struct ptp_ocp_signal	signal[4];
-	struct ptp_ocp_sma_connector sma[4];
+	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -835,6 +849,7 @@ static DEFINE_IDR(ptp_ocp_idr);
 struct ocp_selector {
 	const char *name;
 	int value;
+	u64 frequency;
 };
 
 static const struct ocp_selector ptp_ocp_clock[] = {
@@ -855,31 +870,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
 #define SMA_SELECT_MASK		GENMASK(14, 0)
 
 static const struct ocp_selector ptp_ocp_sma_in[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "PPS2",	.value = 0x0002 },
-	{ .name = "TS1",	.value = 0x0004 },
-	{ .name = "TS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
-	{ .name = "TS3",	.value = 0x0040 },
-	{ .name = "TS4",	.value = 0x0080 },
-	{ .name = "FREQ1",	.value = 0x0100 },
-	{ .name = "FREQ2",	.value = 0x0200 },
-	{ .name = "FREQ3",	.value = 0x0400 },
-	{ .name = "FREQ4",	.value = 0x0800 },
-	{ .name = "None",	.value = SMA_DISABLE },
+	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
+	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
+	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
+	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
+	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
+	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
+	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
+	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
+	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
+	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
+	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
+	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
+	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
+	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_sma_out[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PHC",	.value = 0x0001 },
-	{ .name = "MAC",	.value = 0x0002 },
-	{ .name = "GNSS1",	.value = 0x0004 },
-	{ .name = "GNSS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
+	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
+	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
+	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
+	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
 	{ .name = "GEN1",	.value = 0x0040 },
 	{ .name = "GEN2",	.value = 0x0080 },
 	{ .name = "GEN3",	.value = 0x0100 },
@@ -890,15 +905,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_in[] = {
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "10Mhz",	.value = 0x0008 },
+	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_out[] = {
-	{ .name = "PHC",	.value = 0x0002 },
-	{ .name = "GNSS",	.value = 0x0004 },
-	{ .name = "10Mhz",	.value = 0x0010 },
+	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
 	{ }
 };
 
@@ -1351,7 +1366,6 @@ static int
 ptp_ocp_init_clock(struct ptp_ocp *bp)
 {
 	struct timespec64 ts;
-	bool sync;
 	u32 ctrl;
 
 	ctrl = OCP_CTRL_ENABLE;
@@ -1375,8 +1389,8 @@ ptp_ocp_init_clock(struct ptp_ocp *bp)
 
 	ptp_ocp_estimate_pci_timing(bp);
 
-	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
-	if (!sync) {
+	bp->sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+	if (!bp->sync) {
 		ktime_get_clocktai_ts64(&ts);
 		ptp_ocp_settime(&bp->ptp_info, &ts);
 	}
@@ -2289,22 +2303,35 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
 static void
 ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.board_label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
 	/* defaults */
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].default_fcn = i & 1;
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.board_label =
+			bp->ptp_info.pin_config[i].name;
+	}
 	bp->sma[0].mode = SMA_MODE_IN;
 	bp->sma[1].mode = SMA_MODE_IN;
 	bp->sma[2].mode = SMA_MODE_OUT;
 	bp->sma[3].mode = SMA_MODE_OUT;
-	for (i = 0; i < 4; i++)
-		bp->sma[i].default_fcn = i & 1;
-
 	/* If no SMA1 map, the pin functions and directions are fixed. */
 	if (!bp->sma_map1) {
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < OCP_SMA_NUM; i++) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			bp->sma[1].dpll_prop.capabilities &=
+				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 		}
 		return;
 	}
@@ -2314,7 +2341,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 	 */
 	reg = ioread32(&bp->sma_map2->gpio2);
 	if (reg == 0xffffffff) {
-		for (i = 0; i < 4; i++)
+		for (i = 0; i < OCP_SMA_NUM; i++)
 			bp->sma[i].fixed_dir = true;
 	} else {
 		reg = ioread32(&bp->sma_map1->gpio1);
@@ -2336,7 +2363,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
 };
 
 static int
-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
+ptp_ocp_set_pins(struct ptp_ocp *bp)
 {
 	struct ptp_pin_desc *config;
 	int i;
@@ -2403,16 +2430,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 
 	ptp_ocp_tod_init(bp);
 	ptp_ocp_nmea_out_init(bp);
-	ptp_ocp_sma_init(bp);
 	ptp_ocp_signal_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
 	if (err)
 		return err;
 
-	err = ptp_ocp_fb_set_pins(bp);
+	err = ptp_ocp_set_pins(bp);
 	if (err)
 		return err;
+	ptp_ocp_sma_init(bp);
 
 	return ptp_ocp_init_clock(bp);
 }
@@ -2452,6 +2479,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
 static void
 ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.board_label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = 0,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
@@ -2466,16 +2501,17 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
 
-	/* If no SMA map, the pin functions and directions are fixed. */
-	if (!bp->art_sma) {
-		for (i = 0; i < 4; i++) {
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		/* If no SMA map, the pin functions and directions are fixed. */
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.board_label =
+			bp->ptp_info.pin_config[i].name;
+		if (!bp->art_sma) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			continue;
 		}
-		return;
-	}
-
-	for (i = 0; i < 4; i++) {
 		reg = ioread32(&bp->art_sma->map[i].gpio);
 
 		switch (reg & 0xff) {
@@ -2486,9 +2522,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 		case 1:
 		case 8:
 			bp->sma[i].mode = SMA_MODE_IN;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		default:
 			bp->sma[i].mode = SMA_MODE_OUT;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		}
 	}
@@ -2555,6 +2595,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 	/* Enable MAC serial port during initialisation */
 	iowrite32(1, &bp->board_config->mro50_serial_activate);
 
+	err = ptp_ocp_set_pins(bp);
+	if (err)
+		return err;
 	ptp_ocp_sma_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
@@ -2696,16 +2739,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;
@@ -2740,6 +2776,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
 	return val;
 }
 
+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+	enum ptp_ocp_sma_mode mode;
+	int val;
+
+	mode = sma->mode;
+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+	if (val < 0)
+		return val;
+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
 static ssize_t
 sma1_store(struct device *dev, struct device_attribute *attr,
 	   const char *buf, size_t count)
@@ -3834,9 +3884,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
 		strcpy(buf, "unknown");
 		break;
 	}
-	val = ioread32(&bp->reg->status);
 	seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf,
-		   val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced");
+		   bp->sync ? "sync" : "unsynced");
 
 	if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) {
 		struct timespec64 sys_ts;
@@ -4068,7 +4117,6 @@ ptp_ocp_phc_info(struct ptp_ocp *bp)
 {
 	struct timespec64 ts;
 	u32 version, select;
-	bool sync;
 
 	version = ioread32(&bp->reg->version);
 	select = ioread32(&bp->reg->select);
@@ -4077,11 +4125,10 @@ ptp_ocp_phc_info(struct ptp_ocp *bp)
 		 ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16),
 		 ptp_clock_index(bp->ptp));
 
-	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
 	if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL))
 		dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n",
 			 ts.tv_sec, ts.tv_nsec,
-			 sync ? "in-sync" : "UNSYNCED");
+			 bp->sync ? "in-sync" : "UNSYNCED");
 }
 
 static void
@@ -4178,12 +4225,168 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+					void *priv,
+					enum dpll_lock_status *status,
+					struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+
+	*status = bp->sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
+
+	return 0;
+}
+
+static int ptp_ocp_dpll_state_get(const struct dpll_pin *pin, void *pin_priv,
+				  const struct dpll_device *dpll, void *priv,
+				  enum dpll_pin_state *state,
+				  struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+	int idx;
+
+	if (bp->pps_select) {
+		idx = ioread32(&bp->pps_select->gpio1);
+		*state = (&bp->sma[idx] == pin_priv) ? DPLL_PIN_STATE_CONNECTED :
+						      DPLL_PIN_STATE_SELECTABLE;
+		return 0;
+	}
+	NL_SET_ERR_MSG(extack, "pin selection is not supported on current HW");
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
+				 u32 *mode, struct netlink_ext_ack *extack)
+{
+	*mode = DPLL_MODE_AUTOMATIC;
+	return 0;
+}
+
+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
+					void *priv, const enum dpll_mode mode,
+					struct netlink_ext_ack *extack)
+{
+	return mode == DPLL_MODE_AUTOMATIC;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *priv,
+				      enum dpll_pin_direction *direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+
+	*direction = sma->mode == SMA_MODE_IN ?
+				  DPLL_PIN_DIRECTION_INPUT :
+				  DPLL_PIN_DIRECTION_OUTPUT;
+	return 0;
+}
+
+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv,
+				      enum dpll_pin_direction direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	enum ptp_ocp_sma_mode mode;
+	int sma_nr = (sma - bp->sma);
+
+	if (sma->fixed_dir)
+		return -EOPNOTSUPP;
+	mode = direction == DPLL_PIN_DIRECTION_INPUT ?
+			    SMA_MODE_IN : SMA_MODE_OUT;
+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	int val, i;
+
+	if (sma->fixed_fcn)
+		return -EOPNOTSUPP;
+
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (tbl[i].frequency == frequency)
+			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 *frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	u32 val;
+	int i;
+
+	val = bp->sma_op->get(bp, sma_nr);
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (val == tbl[i].value) {
+			*frequency = tbl[i].frequency;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static const struct dpll_device_ops dpll_ops = {
+	.lock_status_get = ptp_ocp_dpll_lock_status_get,
+	.mode_get = ptp_ocp_dpll_mode_get,
+	.mode_supported = ptp_ocp_dpll_mode_supported,
+};
+
+static const struct dpll_pin_ops dpll_pins_ops = {
+	.frequency_get = ptp_ocp_dpll_frequency_get,
+	.frequency_set = ptp_ocp_dpll_frequency_set,
+	.direction_get = ptp_ocp_dpll_direction_get,
+	.direction_set = ptp_ocp_dpll_direction_set,
+	.state_on_dpll_get = ptp_ocp_dpll_state_get,
+};
+
+static void
+ptp_ocp_sync_work(struct work_struct *work)
+{
+	struct ptp_ocp *bp;
+	bool sync;
+
+	bp = container_of(work, struct ptp_ocp, sync_work.work);
+	sync = !!(ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC);
+
+	if (bp->sync != sync)
+		dpll_device_change_ntf(bp->dpll);
+
+	bp->sync = sync;
+
+	queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ);
+}
+
 static int
 ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct devlink *devlink;
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
+	u64 clkid;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4202,6 +4405,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		goto out_disable;
 
+	INIT_DELAYED_WORK(&bp->sync_work, ptp_ocp_sync_work);
+
 	/* compat mode.
 	 * Older FPGA firmware only returns 2 irq's.
 	 * allow this - if not all of the IRQ's are returned, skip the
@@ -4233,8 +4438,40 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	ptp_ocp_info(bp);
 	devlink_register(devlink);
-	return 0;
 
+	clkid = pci_get_dsn(pdev);
+	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
+	if (IS_ERR(bp->dpll)) {
+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
+		goto out;
+	}
+
+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
+	if (err)
+		goto out;
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
+		if (IS_ERR(bp->sma[i].dpll_pin))
+			goto out_dpll;
+
+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
+					&bp->sma[i]);
+		if (err) {
+			dpll_pin_put(bp->sma[i].dpll_pin);
+			goto out_dpll;
+		}
+	}
+	queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ);
+
+	return 0;
+out_dpll:
+	while (i) {
+		--i;
+		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
+		dpll_pin_put(bp->sma[i].dpll_pin);
+	}
+	dpll_device_put(bp->dpll);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4249,7 +4486,17 @@ ptp_ocp_remove(struct pci_dev *pdev)
 {
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
+	int i;
 
+	cancel_delayed_work_sync(&bp->sync_work);
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		if (bp->sma[i].dpll_pin) {
+			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
+			dpll_pin_put(bp->sma[i].dpll_pin);
+		}
+	}
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
+	dpll_device_put(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
 	pci_disable_device(pdev);
-- 
2.27.0


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

* [PATCH 11/11] mlx5: Implement SyncE support using DPLL infrastructure
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-20  9:19   ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20  9:19 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Jiri Pirko, Milena Olech, Michal Michalik, Vadim Fedorenko,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

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>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 .../net/ethernet/mellanox/mlx5/core/Kconfig   |   8 +
 .../net/ethernet/mellanox/mlx5/core/Makefile  |   3 +
 drivers/net/ethernet/mellanox/mlx5/core/dev.c |  17 +
 .../net/ethernet/mellanox/mlx5/core/dpll.c    | 432 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 520 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index bb1d7b039a7e..15a48d376eb3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -188,3 +188,11 @@ config MLX5_SF_MANAGER
 	port is managed through devlink.  A subfunction supports RDMA, netdevice
 	and vdpa device. It is similar to a SRIOV VF but it doesn't require
 	SRIOV support.
+
+config MLX5_DPLL
+	tristate "Mellanox 5th generation network adapters (ConnectX series) DPLL support"
+	depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
+	select DPLL
+	help
+	  DPLL support in Mellanox Technologies ConnectX NICs.
+
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 35f00700a4d6..3965249c1c34 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -124,3 +124,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 edb06fb9bbc5..948664f0cd3a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -205,6 +205,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,
@@ -214,6 +227,8 @@ enum {
 	MLX5_INTERFACE_PROTOCOL_MPIB,
 
 	MLX5_INTERFACE_PROTOCOL_VNET,
+
+	MLX5_INTERFACE_PROTOCOL_DPLL,
 };
 
 static const struct mlx5_adev_device {
@@ -236,6 +251,8 @@ static const struct mlx5_adev_device {
 					   .is_supported = &is_ib_rep_supported },
 	[MLX5_INTERFACE_PROTOCOL_MPIB] = { .suffix = "multiport",
 					   .is_supported = &is_mp_supported },
+	[MLX5_INTERFACE_PROTOCOL_DPLL] = { .suffix = "dpll",
+					   .is_supported = &is_dpll_supported },
 };
 
 int mlx5_adev_idx_alloc(void)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
new file mode 100644
index 000000000000..9bff7044c614
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/dpll.h>
+#include <linux/mlx5/driver.h>
+
+/* This structure represents a reference to DPLL, one is created
+ * per mdev instance.
+ */
+struct mlx5_dpll {
+	struct dpll_device *dpll;
+	struct dpll_pin *dpll_pin;
+	struct mlx5_core_dev *mdev;
+	struct workqueue_struct *wq;
+	struct delayed_work work;
+	struct {
+		bool valid;
+		enum dpll_lock_status lock_status;
+		enum dpll_pin_state pin_state;
+	} last;
+	struct notifier_block mdev_nb;
+	struct net_device *tracking_netdev;
+};
+
+static int mlx5_dpll_clock_id_get(struct mlx5_core_dev *mdev, u64 *clock_id)
+{
+	u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
+	int err;
+
+	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				   MLX5_REG_MSECQ, 0, 0);
+	if (err)
+		return err;
+	*clock_id = MLX5_GET64(msecq_reg, out, local_clock_identity);
+	return 0;
+}
+
+static int
+mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev,
+			   enum mlx5_msees_admin_status *admin_status,
+			   enum mlx5_msees_oper_status *oper_status,
+			   bool *ho_acq)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+	int err;
+
+	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				   MLX5_REG_MSEES, 0, 0);
+	if (err)
+		return err;
+	if (admin_status)
+		*admin_status = MLX5_GET(msees_reg, out, admin_status);
+	*oper_status = MLX5_GET(msees_reg, out, oper_status);
+	if (ho_acq)
+		*ho_acq = MLX5_GET(msees_reg, out, ho_acq);
+	return 0;
+}
+
+static int
+mlx5_dpll_synce_status_set(struct mlx5_core_dev *mdev,
+			   enum mlx5_msees_admin_status admin_status)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+
+	MLX5_SET(msees_reg, in, field_select,
+		 MLX5_MSEES_FIELD_SELECT_ENABLE |
+		 MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS);
+	MLX5_SET(msees_reg, in, admin_status, admin_status);
+	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				    MLX5_REG_MSEES, 0, 1);
+}
+
+static enum dpll_lock_status
+mlx5_dpll_lock_status_get(enum mlx5_msees_oper_status oper_status, bool ho_acq)
+{
+	switch (oper_status) {
+	case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
+		return ho_acq ? DPLL_LOCK_STATUS_LOCKED_HO_ACQ :
+				DPLL_LOCK_STATUS_LOCKED;
+	case MLX5_MSEES_OPER_STATUS_HOLDOVER:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
+		return DPLL_LOCK_STATUS_HOLDOVER;
+	default:
+		return DPLL_LOCK_STATUS_UNLOCKED;
+	}
+}
+
+static enum dpll_pin_state
+mlx5_dpll_pin_state_get(enum mlx5_msees_admin_status admin_status,
+			enum mlx5_msees_oper_status oper_status)
+{
+	return (admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK &&
+		(oper_status == MLX5_MSEES_OPER_STATUS_SELF_TRACK ||
+		 oper_status == MLX5_MSEES_OPER_STATUS_OTHER_TRACK)) ?
+	       DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
+}
+
+static int mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll,
+					    void *priv,
+					    enum dpll_lock_status *status,
+					    struct netlink_ext_ack *extack)
+{
+	enum mlx5_msees_oper_status oper_status;
+	struct mlx5_dpll *mdpll = priv;
+	bool ho_acq;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, NULL,
+					 &oper_status, &ho_acq);
+	if (err)
+		return err;
+
+	*status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+	return 0;
+}
+
+static int mlx5_dpll_device_mode_get(const struct dpll_device *dpll,
+				     void *priv,
+				     u32 *mode, struct netlink_ext_ack *extack)
+{
+	*mode = DPLL_MODE_MANUAL;
+	return 0;
+}
+
+static bool mlx5_dpll_device_mode_supported(const struct dpll_device *dpll,
+					    void *priv,
+					    enum dpll_mode mode,
+					    struct netlink_ext_ack *extack)
+{
+	return mode == DPLL_MODE_MANUAL;
+}
+
+static const struct dpll_device_ops mlx5_dpll_device_ops = {
+	.lock_status_get = mlx5_dpll_device_lock_status_get,
+	.mode_get = mlx5_dpll_device_mode_get,
+	.mode_supported = mlx5_dpll_device_mode_supported,
+};
+
+static int mlx5_dpll_pin_direction_get(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_direction *direction,
+				       struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_INPUT;
+	return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_get(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_state *state,
+				       struct netlink_ext_ack *extack)
+{
+	enum mlx5_msees_admin_status admin_status;
+	enum mlx5_msees_oper_status oper_status;
+	struct mlx5_dpll *mdpll = pin_priv;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status, NULL);
+	if (err)
+		return err;
+	*state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+	return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_state state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = pin_priv;
+
+	return mlx5_dpll_synce_status_set(mdpll->mdev,
+					  state == DPLL_PIN_STATE_CONNECTED ?
+					  MLX5_MSEES_ADMIN_STATUS_TRACK :
+					  MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
+	.direction_get = mlx5_dpll_pin_direction_get,
+	.state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
+	.state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
+};
+
+static const struct dpll_pin_properties mlx5_dpll_pin_properties = {
+	.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+	.capabilities = DPLL_PIN_CAPS_STATE_CAN_CHANGE,
+};
+
+#define MLX5_DPLL_PERIODIC_WORK_INTERVAL 500 /* ms */
+
+static void mlx5_dpll_periodic_work_queue(struct mlx5_dpll *mdpll)
+{
+	queue_delayed_work(mdpll->wq, &mdpll->work,
+			   msecs_to_jiffies(MLX5_DPLL_PERIODIC_WORK_INTERVAL));
+}
+
+static void mlx5_dpll_periodic_work(struct work_struct *work)
+{
+	struct mlx5_dpll *mdpll = container_of(work, struct mlx5_dpll,
+					       work.work);
+	enum mlx5_msees_admin_status admin_status;
+	enum mlx5_msees_oper_status oper_status;
+	enum dpll_lock_status lock_status;
+	enum dpll_pin_state pin_state;
+	bool ho_acq;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status, &ho_acq);
+	if (err)
+		goto err_out;
+	lock_status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+	pin_state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+
+	if (!mdpll->last.valid)
+		goto invalid_out;
+
+	if (mdpll->last.lock_status != lock_status)
+		dpll_device_change_ntf(mdpll->dpll);
+	if (mdpll->last.pin_state != pin_state)
+		dpll_pin_change_ntf(mdpll->dpll_pin);
+
+invalid_out:
+	mdpll->last.lock_status = lock_status;
+	mdpll->last.pin_state = pin_state;
+	mdpll->last.valid = true;
+err_out:
+	mlx5_dpll_periodic_work_queue(mdpll);
+}
+
+static void mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll *mdpll,
+					  struct net_device *netdev)
+{
+	if (mdpll->tracking_netdev)
+		return;
+	netdev_dpll_pin_set(netdev, mdpll->dpll_pin);
+	mdpll->tracking_netdev = netdev;
+}
+
+static void mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll *mdpll)
+{
+	if (!mdpll->tracking_netdev)
+		return;
+	netdev_dpll_pin_clear(mdpll->tracking_netdev);
+	mdpll->tracking_netdev = NULL;
+}
+
+static int mlx5_dpll_mdev_notifier_event(struct notifier_block *nb,
+					 unsigned long event, void *data)
+{
+	struct mlx5_dpll *mdpll = container_of(nb, struct mlx5_dpll, mdev_nb);
+	struct net_device *netdev = data;
+
+	switch (event) {
+	case MLX5_DRIVER_EVENT_UPLINK_NETDEV:
+		if (netdev)
+			mlx5_dpll_netdev_dpll_pin_set(mdpll, netdev);
+		else
+			mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static void mlx5_dpll_mdev_netdev_track(struct mlx5_dpll *mdpll,
+					struct mlx5_core_dev *mdev)
+{
+	mdpll->mdev_nb.notifier_call = mlx5_dpll_mdev_notifier_event;
+	mlx5_blocking_notifier_register(mdev, &mdpll->mdev_nb);
+	mlx5_core_uplink_netdev_event_replay(mdev);
+}
+
+static void mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll *mdpll,
+					  struct mlx5_core_dev *mdev)
+{
+	mlx5_blocking_notifier_unregister(mdev, &mdpll->mdev_nb);
+	mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+}
+
+static int mlx5_dpll_probe(struct auxiliary_device *adev,
+			   const struct auxiliary_device_id *id)
+{
+	struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
+	struct mlx5_core_dev *mdev = edev->mdev;
+	struct mlx5_dpll *mdpll;
+	u64 clock_id;
+	int err;
+
+	err = mlx5_dpll_synce_status_set(mdev,
+					 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+	if (err)
+		return err;
+
+	err = mlx5_dpll_clock_id_get(mdev, &clock_id);
+	if (err)
+		return err;
+
+	mdpll = kzalloc(sizeof(*mdpll), GFP_KERNEL);
+	if (!mdpll)
+		return -ENOMEM;
+	mdpll->mdev = mdev;
+	auxiliary_set_drvdata(adev, mdpll);
+
+	/* Multiple mdev instances might share one DPLL device. */
+	mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
+	if (IS_ERR(mdpll->dpll)) {
+		err = PTR_ERR(mdpll->dpll);
+		goto err_free_mdpll;
+	}
+
+	err = dpll_device_register(mdpll->dpll, DPLL_TYPE_EEC,
+				   &mlx5_dpll_device_ops, mdpll);
+	if (err)
+		goto err_put_dpll_device;
+
+	/* Multiple mdev instances might share one DPLL pin. */
+	mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
+				       THIS_MODULE, &mlx5_dpll_pin_properties);
+	if (IS_ERR(mdpll->dpll_pin)) {
+		err = PTR_ERR(mdpll->dpll_pin);
+		goto err_unregister_dpll_device;
+	}
+
+	err = dpll_pin_register(mdpll->dpll, mdpll->dpll_pin,
+				&mlx5_dpll_pins_ops, mdpll);
+	if (err)
+		goto err_put_dpll_pin;
+
+	mdpll->wq = create_singlethread_workqueue("mlx5_dpll");
+	if (!mdpll->wq) {
+		err = -ENOMEM;
+		goto err_unregister_dpll_pin;
+	}
+
+	mlx5_dpll_mdev_netdev_track(mdpll, mdev);
+
+	INIT_DELAYED_WORK(&mdpll->work, &mlx5_dpll_periodic_work);
+	mlx5_dpll_periodic_work_queue(mdpll);
+
+	return 0;
+
+err_unregister_dpll_pin:
+	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+			    &mlx5_dpll_pins_ops, mdpll);
+err_put_dpll_pin:
+	dpll_pin_put(mdpll->dpll_pin);
+err_unregister_dpll_device:
+	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+err_put_dpll_device:
+	dpll_device_put(mdpll->dpll);
+err_free_mdpll:
+	kfree(mdpll);
+	return err;
+}
+
+static void mlx5_dpll_remove(struct auxiliary_device *adev)
+{
+	struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev);
+	struct mlx5_core_dev *mdev = mdpll->mdev;
+
+	cancel_delayed_work(&mdpll->work);
+	mlx5_dpll_mdev_netdev_untrack(mdpll, mdev);
+	destroy_workqueue(mdpll->wq);
+	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+			    &mlx5_dpll_pins_ops, mdpll);
+	dpll_pin_put(mdpll->dpll_pin);
+	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+	dpll_device_put(mdpll->dpll);
+	kfree(mdpll);
+
+	mlx5_dpll_synce_status_set(mdev,
+				   MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)
+{
+	return 0;
+}
+
+static int mlx5_dpll_resume(struct auxiliary_device *adev)
+{
+	return 0;
+}
+
+static const struct auxiliary_device_id mlx5_dpll_id_table[] = {
+	{ .name = MLX5_ADEV_NAME ".dpll", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);
+
+static struct auxiliary_driver mlx5_dpll_driver = {
+	.name = "dpll",
+	.probe = mlx5_dpll_probe,
+	.remove = mlx5_dpll_remove,
+	.suspend = mlx5_dpll_suspend,
+	.resume = mlx5_dpll_resume,
+	.id_table = mlx5_dpll_id_table,
+};
+
+static int __init mlx5_dpll_init(void)
+{
+	return auxiliary_driver_register(&mlx5_dpll_driver);
+}
+
+static void __exit mlx5_dpll_exit(void)
+{
+	auxiliary_driver_unregister(&mlx5_dpll_driver);
+}
+
+module_init(mlx5_dpll_init);
+module_exit(mlx5_dpll_exit);
+
+MODULE_AUTHOR("Jiri Pirko <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 25d0528f9219..d0c85e16fa45 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 33344a71c3e3..a231663a8074 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10211,7 +10211,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];
 
@@ -12573,4 +12575,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.27.0


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

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

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>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 .../net/ethernet/mellanox/mlx5/core/Kconfig   |   8 +
 .../net/ethernet/mellanox/mlx5/core/Makefile  |   3 +
 drivers/net/ethernet/mellanox/mlx5/core/dev.c |  17 +
 .../net/ethernet/mellanox/mlx5/core/dpll.c    | 432 ++++++++++++++++++
 include/linux/mlx5/driver.h                   |   2 +
 include/linux/mlx5/mlx5_ifc.h                 |  59 ++-
 6 files changed, 520 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index bb1d7b039a7e..15a48d376eb3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -188,3 +188,11 @@ config MLX5_SF_MANAGER
 	port is managed through devlink.  A subfunction supports RDMA, netdevice
 	and vdpa device. It is similar to a SRIOV VF but it doesn't require
 	SRIOV support.
+
+config MLX5_DPLL
+	tristate "Mellanox 5th generation network adapters (ConnectX series) DPLL support"
+	depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
+	select DPLL
+	help
+	  DPLL support in Mellanox Technologies ConnectX NICs.
+
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 35f00700a4d6..3965249c1c34 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -124,3 +124,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 edb06fb9bbc5..948664f0cd3a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -205,6 +205,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,
@@ -214,6 +227,8 @@ enum {
 	MLX5_INTERFACE_PROTOCOL_MPIB,
 
 	MLX5_INTERFACE_PROTOCOL_VNET,
+
+	MLX5_INTERFACE_PROTOCOL_DPLL,
 };
 
 static const struct mlx5_adev_device {
@@ -236,6 +251,8 @@ static const struct mlx5_adev_device {
 					   .is_supported = &is_ib_rep_supported },
 	[MLX5_INTERFACE_PROTOCOL_MPIB] = { .suffix = "multiport",
 					   .is_supported = &is_mp_supported },
+	[MLX5_INTERFACE_PROTOCOL_DPLL] = { .suffix = "dpll",
+					   .is_supported = &is_dpll_supported },
 };
 
 int mlx5_adev_idx_alloc(void)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
new file mode 100644
index 000000000000..9bff7044c614
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/dpll.h>
+#include <linux/mlx5/driver.h>
+
+/* This structure represents a reference to DPLL, one is created
+ * per mdev instance.
+ */
+struct mlx5_dpll {
+	struct dpll_device *dpll;
+	struct dpll_pin *dpll_pin;
+	struct mlx5_core_dev *mdev;
+	struct workqueue_struct *wq;
+	struct delayed_work work;
+	struct {
+		bool valid;
+		enum dpll_lock_status lock_status;
+		enum dpll_pin_state pin_state;
+	} last;
+	struct notifier_block mdev_nb;
+	struct net_device *tracking_netdev;
+};
+
+static int mlx5_dpll_clock_id_get(struct mlx5_core_dev *mdev, u64 *clock_id)
+{
+	u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
+	int err;
+
+	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				   MLX5_REG_MSECQ, 0, 0);
+	if (err)
+		return err;
+	*clock_id = MLX5_GET64(msecq_reg, out, local_clock_identity);
+	return 0;
+}
+
+static int
+mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev,
+			   enum mlx5_msees_admin_status *admin_status,
+			   enum mlx5_msees_oper_status *oper_status,
+			   bool *ho_acq)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+	int err;
+
+	err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				   MLX5_REG_MSEES, 0, 0);
+	if (err)
+		return err;
+	if (admin_status)
+		*admin_status = MLX5_GET(msees_reg, out, admin_status);
+	*oper_status = MLX5_GET(msees_reg, out, oper_status);
+	if (ho_acq)
+		*ho_acq = MLX5_GET(msees_reg, out, ho_acq);
+	return 0;
+}
+
+static int
+mlx5_dpll_synce_status_set(struct mlx5_core_dev *mdev,
+			   enum mlx5_msees_admin_status admin_status)
+{
+	u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+	u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+
+	MLX5_SET(msees_reg, in, field_select,
+		 MLX5_MSEES_FIELD_SELECT_ENABLE |
+		 MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS);
+	MLX5_SET(msees_reg, in, admin_status, admin_status);
+	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+				    MLX5_REG_MSEES, 0, 1);
+}
+
+static enum dpll_lock_status
+mlx5_dpll_lock_status_get(enum mlx5_msees_oper_status oper_status, bool ho_acq)
+{
+	switch (oper_status) {
+	case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
+		return ho_acq ? DPLL_LOCK_STATUS_LOCKED_HO_ACQ :
+				DPLL_LOCK_STATUS_LOCKED;
+	case MLX5_MSEES_OPER_STATUS_HOLDOVER:
+		fallthrough;
+	case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
+		return DPLL_LOCK_STATUS_HOLDOVER;
+	default:
+		return DPLL_LOCK_STATUS_UNLOCKED;
+	}
+}
+
+static enum dpll_pin_state
+mlx5_dpll_pin_state_get(enum mlx5_msees_admin_status admin_status,
+			enum mlx5_msees_oper_status oper_status)
+{
+	return (admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK &&
+		(oper_status == MLX5_MSEES_OPER_STATUS_SELF_TRACK ||
+		 oper_status == MLX5_MSEES_OPER_STATUS_OTHER_TRACK)) ?
+	       DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
+}
+
+static int mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll,
+					    void *priv,
+					    enum dpll_lock_status *status,
+					    struct netlink_ext_ack *extack)
+{
+	enum mlx5_msees_oper_status oper_status;
+	struct mlx5_dpll *mdpll = priv;
+	bool ho_acq;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, NULL,
+					 &oper_status, &ho_acq);
+	if (err)
+		return err;
+
+	*status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+	return 0;
+}
+
+static int mlx5_dpll_device_mode_get(const struct dpll_device *dpll,
+				     void *priv,
+				     u32 *mode, struct netlink_ext_ack *extack)
+{
+	*mode = DPLL_MODE_MANUAL;
+	return 0;
+}
+
+static bool mlx5_dpll_device_mode_supported(const struct dpll_device *dpll,
+					    void *priv,
+					    enum dpll_mode mode,
+					    struct netlink_ext_ack *extack)
+{
+	return mode == DPLL_MODE_MANUAL;
+}
+
+static const struct dpll_device_ops mlx5_dpll_device_ops = {
+	.lock_status_get = mlx5_dpll_device_lock_status_get,
+	.mode_get = mlx5_dpll_device_mode_get,
+	.mode_supported = mlx5_dpll_device_mode_supported,
+};
+
+static int mlx5_dpll_pin_direction_get(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_direction *direction,
+				       struct netlink_ext_ack *extack)
+{
+	*direction = DPLL_PIN_DIRECTION_INPUT;
+	return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_get(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_state *state,
+				       struct netlink_ext_ack *extack)
+{
+	enum mlx5_msees_admin_status admin_status;
+	enum mlx5_msees_oper_status oper_status;
+	struct mlx5_dpll *mdpll = pin_priv;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status, NULL);
+	if (err)
+		return err;
+	*state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+	return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
+				       void *pin_priv,
+				       const struct dpll_device *dpll,
+				       void *dpll_priv,
+				       enum dpll_pin_state state,
+				       struct netlink_ext_ack *extack)
+{
+	struct mlx5_dpll *mdpll = pin_priv;
+
+	return mlx5_dpll_synce_status_set(mdpll->mdev,
+					  state == DPLL_PIN_STATE_CONNECTED ?
+					  MLX5_MSEES_ADMIN_STATUS_TRACK :
+					  MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
+	.direction_get = mlx5_dpll_pin_direction_get,
+	.state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
+	.state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
+};
+
+static const struct dpll_pin_properties mlx5_dpll_pin_properties = {
+	.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+	.capabilities = DPLL_PIN_CAPS_STATE_CAN_CHANGE,
+};
+
+#define MLX5_DPLL_PERIODIC_WORK_INTERVAL 500 /* ms */
+
+static void mlx5_dpll_periodic_work_queue(struct mlx5_dpll *mdpll)
+{
+	queue_delayed_work(mdpll->wq, &mdpll->work,
+			   msecs_to_jiffies(MLX5_DPLL_PERIODIC_WORK_INTERVAL));
+}
+
+static void mlx5_dpll_periodic_work(struct work_struct *work)
+{
+	struct mlx5_dpll *mdpll = container_of(work, struct mlx5_dpll,
+					       work.work);
+	enum mlx5_msees_admin_status admin_status;
+	enum mlx5_msees_oper_status oper_status;
+	enum dpll_lock_status lock_status;
+	enum dpll_pin_state pin_state;
+	bool ho_acq;
+	int err;
+
+	err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+					 &oper_status, &ho_acq);
+	if (err)
+		goto err_out;
+	lock_status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+	pin_state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+
+	if (!mdpll->last.valid)
+		goto invalid_out;
+
+	if (mdpll->last.lock_status != lock_status)
+		dpll_device_change_ntf(mdpll->dpll);
+	if (mdpll->last.pin_state != pin_state)
+		dpll_pin_change_ntf(mdpll->dpll_pin);
+
+invalid_out:
+	mdpll->last.lock_status = lock_status;
+	mdpll->last.pin_state = pin_state;
+	mdpll->last.valid = true;
+err_out:
+	mlx5_dpll_periodic_work_queue(mdpll);
+}
+
+static void mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll *mdpll,
+					  struct net_device *netdev)
+{
+	if (mdpll->tracking_netdev)
+		return;
+	netdev_dpll_pin_set(netdev, mdpll->dpll_pin);
+	mdpll->tracking_netdev = netdev;
+}
+
+static void mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll *mdpll)
+{
+	if (!mdpll->tracking_netdev)
+		return;
+	netdev_dpll_pin_clear(mdpll->tracking_netdev);
+	mdpll->tracking_netdev = NULL;
+}
+
+static int mlx5_dpll_mdev_notifier_event(struct notifier_block *nb,
+					 unsigned long event, void *data)
+{
+	struct mlx5_dpll *mdpll = container_of(nb, struct mlx5_dpll, mdev_nb);
+	struct net_device *netdev = data;
+
+	switch (event) {
+	case MLX5_DRIVER_EVENT_UPLINK_NETDEV:
+		if (netdev)
+			mlx5_dpll_netdev_dpll_pin_set(mdpll, netdev);
+		else
+			mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static void mlx5_dpll_mdev_netdev_track(struct mlx5_dpll *mdpll,
+					struct mlx5_core_dev *mdev)
+{
+	mdpll->mdev_nb.notifier_call = mlx5_dpll_mdev_notifier_event;
+	mlx5_blocking_notifier_register(mdev, &mdpll->mdev_nb);
+	mlx5_core_uplink_netdev_event_replay(mdev);
+}
+
+static void mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll *mdpll,
+					  struct mlx5_core_dev *mdev)
+{
+	mlx5_blocking_notifier_unregister(mdev, &mdpll->mdev_nb);
+	mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+}
+
+static int mlx5_dpll_probe(struct auxiliary_device *adev,
+			   const struct auxiliary_device_id *id)
+{
+	struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
+	struct mlx5_core_dev *mdev = edev->mdev;
+	struct mlx5_dpll *mdpll;
+	u64 clock_id;
+	int err;
+
+	err = mlx5_dpll_synce_status_set(mdev,
+					 MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+	if (err)
+		return err;
+
+	err = mlx5_dpll_clock_id_get(mdev, &clock_id);
+	if (err)
+		return err;
+
+	mdpll = kzalloc(sizeof(*mdpll), GFP_KERNEL);
+	if (!mdpll)
+		return -ENOMEM;
+	mdpll->mdev = mdev;
+	auxiliary_set_drvdata(adev, mdpll);
+
+	/* Multiple mdev instances might share one DPLL device. */
+	mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
+	if (IS_ERR(mdpll->dpll)) {
+		err = PTR_ERR(mdpll->dpll);
+		goto err_free_mdpll;
+	}
+
+	err = dpll_device_register(mdpll->dpll, DPLL_TYPE_EEC,
+				   &mlx5_dpll_device_ops, mdpll);
+	if (err)
+		goto err_put_dpll_device;
+
+	/* Multiple mdev instances might share one DPLL pin. */
+	mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
+				       THIS_MODULE, &mlx5_dpll_pin_properties);
+	if (IS_ERR(mdpll->dpll_pin)) {
+		err = PTR_ERR(mdpll->dpll_pin);
+		goto err_unregister_dpll_device;
+	}
+
+	err = dpll_pin_register(mdpll->dpll, mdpll->dpll_pin,
+				&mlx5_dpll_pins_ops, mdpll);
+	if (err)
+		goto err_put_dpll_pin;
+
+	mdpll->wq = create_singlethread_workqueue("mlx5_dpll");
+	if (!mdpll->wq) {
+		err = -ENOMEM;
+		goto err_unregister_dpll_pin;
+	}
+
+	mlx5_dpll_mdev_netdev_track(mdpll, mdev);
+
+	INIT_DELAYED_WORK(&mdpll->work, &mlx5_dpll_periodic_work);
+	mlx5_dpll_periodic_work_queue(mdpll);
+
+	return 0;
+
+err_unregister_dpll_pin:
+	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+			    &mlx5_dpll_pins_ops, mdpll);
+err_put_dpll_pin:
+	dpll_pin_put(mdpll->dpll_pin);
+err_unregister_dpll_device:
+	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+err_put_dpll_device:
+	dpll_device_put(mdpll->dpll);
+err_free_mdpll:
+	kfree(mdpll);
+	return err;
+}
+
+static void mlx5_dpll_remove(struct auxiliary_device *adev)
+{
+	struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev);
+	struct mlx5_core_dev *mdev = mdpll->mdev;
+
+	cancel_delayed_work(&mdpll->work);
+	mlx5_dpll_mdev_netdev_untrack(mdpll, mdev);
+	destroy_workqueue(mdpll->wq);
+	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+			    &mlx5_dpll_pins_ops, mdpll);
+	dpll_pin_put(mdpll->dpll_pin);
+	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+	dpll_device_put(mdpll->dpll);
+	kfree(mdpll);
+
+	mlx5_dpll_synce_status_set(mdev,
+				   MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)
+{
+	return 0;
+}
+
+static int mlx5_dpll_resume(struct auxiliary_device *adev)
+{
+	return 0;
+}
+
+static const struct auxiliary_device_id mlx5_dpll_id_table[] = {
+	{ .name = MLX5_ADEV_NAME ".dpll", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);
+
+static struct auxiliary_driver mlx5_dpll_driver = {
+	.name = "dpll",
+	.probe = mlx5_dpll_probe,
+	.remove = mlx5_dpll_remove,
+	.suspend = mlx5_dpll_suspend,
+	.resume = mlx5_dpll_resume,
+	.id_table = mlx5_dpll_id_table,
+};
+
+static int __init mlx5_dpll_init(void)
+{
+	return auxiliary_driver_register(&mlx5_dpll_driver);
+}
+
+static void __exit mlx5_dpll_exit(void)
+{
+	auxiliary_driver_unregister(&mlx5_dpll_driver);
+}
+
+module_init(mlx5_dpll_init);
+module_exit(mlx5_dpll_exit);
+
+MODULE_AUTHOR("Jiri Pirko <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 25d0528f9219..d0c85e16fa45 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 33344a71c3e3..a231663a8074 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10211,7 +10211,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];
 
@@ -12573,4 +12575,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.27.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] 109+ messages in thread

* Re: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
  2023-07-20  9:18   ` Vadim Fedorenko
@ 2023-07-20 13:40     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 13:40 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Remove wrong index adjustement, which is leftover from adding

s/adjustement/adjustment/


>support for sparse enums.
>enum.entries_by_val() function shall not subtract the start-value, as
>it is indexed with real enum value.
>
>Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic CLI")
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

Reviewed-by: Jiri Pirko <jiri@nvidia.com>

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

* Re: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
@ 2023-07-20 13:40     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 13:40 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Remove wrong index adjustement, which is leftover from adding

s/adjustement/adjustment/


>support for sparse enums.
>enum.entries_by_val() function shall not subtract the start-value, as
>it is indexed with real enum value.
>
>Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic CLI")
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

Reviewed-by: Jiri Pirko <jiri@nvidia.com>

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

* Re: [PATCH net-next 03/11] dpll: documentation on DPLL subsystem interface
  2023-07-20  9:18   ` Vadim Fedorenko
@ 2023-07-20 13:47     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 13:47 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche,
	Bagas Sanjaya

Thu, Jul 20, 2023 at 11:18:55AM CEST, vadim.fedorenko@linux.dev 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.
>
>Co-developed-by: Bagas Sanjaya <bagasdotme@gmail.com>
>Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>---
>RFC v9->v0:
>- fix muxed get-pin command respond example
>- fix typos
>- describe DPLL_MODE_FREERUN

Yet, there is no mention of "freerun" mode at all :/ Could you please
add it as you state here?

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

* Re: [PATCH net-next 03/11] dpll: documentation on DPLL subsystem interface
@ 2023-07-20 13:47     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 13:47 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche,
	Bagas Sanjaya

Thu, Jul 20, 2023 at 11:18:55AM CEST, vadim.fedorenko@linux.dev 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.
>
>Co-developed-by: Bagas Sanjaya <bagasdotme@gmail.com>
>Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>---
>RFC v9->v0:
>- fix muxed get-pin command respond example
>- fix typos
>- describe DPLL_MODE_FREERUN

Yet, there is no mention of "freerun" mode at all :/ Could you please
add it as you state 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] 109+ messages in thread

* RE: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
  2023-07-20 13:40     ` Jiri Pirko
@ 2023-07-20 13:58       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-20 13:58 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>-----Original Message-----
>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, July 20, 2023 3:40 PM
>
>Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>Remove wrong index adjustement, which is leftover from adding
>
>s/adjustement/adjustment/
>

Sure will fix,
Although those "tools: ynl" patches were not intended to be a part of dpll
Series, they are being discussed on the other thread:
https://lore.kernel.org/netdev/20230718162225.231775-1-arkadiusz.kubalewski@intel.com/

I think Vadim have sent them, because I included them in the branch candidate
for next version, seems was not clear enough on that..
I think we can skip them for next submission.

Thank you!
Arkadiusz

>
>>support for sparse enums.
>>enum.entries_by_val() function shall not subtract the start-value, as
>>it is indexed with real enum value.
>>
>>Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic
>>CLI")
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>Reviewed-by: Jiri Pirko <jiri@nvidia.com>


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

* RE: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
@ 2023-07-20 13:58       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-20 13:58 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>-----Original Message-----
>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, July 20, 2023 3:40 PM
>
>Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>Remove wrong index adjustement, which is leftover from adding
>
>s/adjustement/adjustment/
>

Sure will fix,
Although those "tools: ynl" patches were not intended to be a part of dpll
Series, they are being discussed on the other thread:
https://lore.kernel.org/netdev/20230718162225.231775-1-arkadiusz.kubalewski@intel.com/

I think Vadim have sent them, because I included them in the branch candidate
for next version, seems was not clear enough on that..
I think we can skip them for next submission.

Thank you!
Arkadiusz

>
>>support for sparse enums.
>>enum.entries_by_val() function shall not subtract the start-value, as
>>it is indexed with real enum value.
>>
>>Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic
>>CLI")
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>Reviewed-by: Jiri Pirko <jiri@nvidia.com>


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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20  9:19   ` Vadim Fedorenko
@ 2023-07-20 14:08     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 14:08 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>+/**
>+ * 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
>+ * @extack: error reporting
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    enum ice_dpll_pin_type pin_type,
>+		    struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+



I don't follow. Howcome you don't check if the mode is freerun here or
not? Is it valid to enable a pin when freerun mode? What happens?

Also, I am probably slow, but I still don't see anywhere in this
patchset any description about why we need the freerun mode. What is
diffrerent between:
1) freerun mode
2) automatic mode & all pins disabled?

Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
needs to be documented, please.



Another question, I asked the last time as well, but was not heard:
Consider example where you have 2 netdevices, eth0 and eth1, each
connected with a single DPLL pin:
eth0 - DPLL pin 10 (DPLL device id 2)
eth1 - DPLL pin 11 (DPLL device id 2)

You have a SyncE daemon running on top eth0 and eth1.

Could you please describe following 2 flows?

1) SyncE daemon selects eth0 as a source of clock
2) SyncE daemon selects eth1 as a source of clock


For mlx5 it goes like:

DPLL device mode is MANUAL.
1)
 SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
    -> pin_id: 10
 SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
    -> device_id: 2
 SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state = CONNECTED

2)
 SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
    -> pin_id: 11
 SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
    -> device_id: 2
 SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state = CONNECTED
 (that will in HW disconnect previously connected pin 10, there will be
  notification of pin_id 10, device_id -> state DISCONNECT)


Thanks!


[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-20 14:08     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-20 14:08 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>+/**
>+ * 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
>+ * @extack: error reporting
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    enum ice_dpll_pin_type pin_type,
>+		    struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+



I don't follow. Howcome you don't check if the mode is freerun here or
not? Is it valid to enable a pin when freerun mode? What happens?

Also, I am probably slow, but I still don't see anywhere in this
patchset any description about why we need the freerun mode. What is
diffrerent between:
1) freerun mode
2) automatic mode & all pins disabled?

Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
needs to be documented, please.



Another question, I asked the last time as well, but was not heard:
Consider example where you have 2 netdevices, eth0 and eth1, each
connected with a single DPLL pin:
eth0 - DPLL pin 10 (DPLL device id 2)
eth1 - DPLL pin 11 (DPLL device id 2)

You have a SyncE daemon running on top eth0 and eth1.

Could you please describe following 2 flows?

1) SyncE daemon selects eth0 as a source of clock
2) SyncE daemon selects eth1 as a source of clock


For mlx5 it goes like:

DPLL device mode is MANUAL.
1)
 SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
    -> pin_id: 10
 SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
    -> device_id: 2
 SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state = CONNECTED

2)
 SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
    -> pin_id: 11
 SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
    -> device_id: 2
 SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state = CONNECTED
 (that will in HW disconnect previously connected pin 10, there will be
  notification of pin_id 10, device_id -> state DISCONNECT)


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

* Re: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
  2023-07-20 13:58       ` Kubalewski, Arkadiusz
@ 2023-07-20 14:48         ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20 14:48 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

On 20.07.2023 14:58, Kubalewski, Arkadiusz wrote:
>> -----Original Message-----
>> From: Jiri Pirko <jiri@resnulli.us>
>> Sent: Thursday, July 20, 2023 3:40 PM
>>
>> Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>>> From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>> Remove wrong index adjustement, which is leftover from adding
>>
>> s/adjustement/adjustment/
>>
> 
> Sure will fix,
> Although those "tools: ynl" patches were not intended to be a part of dpll
> Series, they are being discussed on the other thread:
> https://lore.kernel.org/netdev/20230718162225.231775-1-arkadiusz.kubalewski@intel.com/
> 
> I think Vadim have sent them, because I included them in the branch candidate
> for next version, seems was not clear enough on that..
> I think we can skip them for next submission.

Yeah, I just realised that these patches have been sent earlier as separate
series and are still under review. Once they are committed I'll remove them
from DPLL series, but for now they are needed for spec generation of DPLL
yaml, so it's good to have them anyway.

> Thank you!
> Arkadiusz
> 
>>
>>> support for sparse enums.
>>> enum.entries_by_val() function shall not subtract the start-value, as
>>> it is indexed with real enum value.
>>>
>>> Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic
>>> CLI")
>>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> Reviewed-by: Jiri Pirko <jiri@nvidia.com>
> 


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

* Re: [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..)
@ 2023-07-20 14:48         ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-20 14:48 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

On 20.07.2023 14:58, Kubalewski, Arkadiusz wrote:
>> -----Original Message-----
>> From: Jiri Pirko <jiri@resnulli.us>
>> Sent: Thursday, July 20, 2023 3:40 PM
>>
>> Thu, Jul 20, 2023 at 11:18:53AM CEST, vadim.fedorenko@linux.dev wrote:
>>> From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>> Remove wrong index adjustement, which is leftover from adding
>>
>> s/adjustement/adjustment/
>>
> 
> Sure will fix,
> Although those "tools: ynl" patches were not intended to be a part of dpll
> Series, they are being discussed on the other thread:
> https://lore.kernel.org/netdev/20230718162225.231775-1-arkadiusz.kubalewski@intel.com/
> 
> I think Vadim have sent them, because I included them in the branch candidate
> for next version, seems was not clear enough on that..
> I think we can skip them for next submission.

Yeah, I just realised that these patches have been sent earlier as separate
series and are still under review. Once they are committed I'll remove them
from DPLL series, but for now they are needed for spec generation of DPLL
yaml, so it's good to have them anyway.

> Thank you!
> Arkadiusz
> 
>>
>>> support for sparse enums.
>>> enum.entries_by_val() function shall not subtract the start-value, as
>>> it is indexed with real enum value.
>>>
>>> Fixes: c311aaa74ca1 ("tools: ynl: fix enum-as-flags in the generic
>>> CLI")
>>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> Reviewed-by: Jiri Pirko <jiri@nvidia.com>
> 


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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20 14:08     ` Jiri Pirko
@ 2023-07-20 17:31       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-20 17:31 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, July 20, 2023 4:09 PM
>
>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>+/**
>>+ * 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
>>+ * @extack: error reporting
>>+ *
>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    enum ice_dpll_pin_type pin_type,
>>+		    struct netlink_ext_ack *extack)
>>+{
>>+	u8 flags = 0;
>>+	int ret;
>>+
>
>
>
>I don't follow. Howcome you don't check if the mode is freerun here or
>not? Is it valid to enable a pin when freerun mode? What happens?
>

Because you are probably still thinking the modes are somehow connected
to the state of the pin, but it is the other way around.
The dpll device mode is a state of DPLL before pins are even considered.
If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
any of the pins.

>Also, I am probably slow, but I still don't see anywhere in this
>patchset any description about why we need the freerun mode. What is
>diffrerent between:
>1) freerun mode
>2) automatic mode & all pins disabled?

The difference:
Case I:
1. set dpll to FREERUN and configure the source as if it would be in
AUTOMATIC
2. switch to AUTOMATIC
3. connecting to the valid source takes ~50 seconds

Case II:
1. set dpll to AUTOMATIC, set all the source to disconnected
2. switch one valid source to SELECTABLE
3. connecting to the valid source takes ~10 seconds

Basically in AUTOMATIC mode the sources are still monitored even when they
are not in SELECTABLE state, while in FREERUN there is no such monitoring,
so in the end process of synchronizing with the source takes much longer as
dpll need to start the process from scratch.

>
>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>needs to be documented, please.
>

Sure will add the description of FREERUN to the docs.

>
>
>Another question, I asked the last time as well, but was not heard:
>Consider example where you have 2 netdevices, eth0 and eth1, each
>connected with a single DPLL pin:
>eth0 - DPLL pin 10 (DPLL device id 2)
>eth1 - DPLL pin 11 (DPLL device id 2)
>
>You have a SyncE daemon running on top eth0 and eth1.
>
>Could you please describe following 2 flows?
>
>1) SyncE daemon selects eth0 as a source of clock
>2) SyncE daemon selects eth1 as a source of clock
>
>
>For mlx5 it goes like:
>
>DPLL device mode is MANUAL.
>1)
> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>    -> pin_id: 10
> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>    -> device_id: 2

Not sure if it needs to obtain the dpll id in this step, but it doesn't
relate to the dpll interface..

> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>CONNECTED
>
>2)
> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>    -> pin_id: 11
> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>    -> device_id: 2
> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>CONNECTED
> (that will in HW disconnect previously connected pin 10, there will be
>  notification of pin_id 10, device_id -> state DISCONNECT)
>

This flow is similar for ice, but there are some differences, although
they come from the fact, the ice is using AUTOMATIC mode and recovered
clock pins which are not directly connected to a dpll (connected through
the MUX pin).

1) 
a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 13
b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
   (in case of dpll_id is needed, would be find in this response also)
c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
   other pins shall be lower prio i.e. pin-prio:1)
d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
   parent pin (pin-id:2)
 
2) (basically the same, only eth1 would get different pin_id.)
a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 14
b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
   other pins shall be lower prio i.e. pin-prio:1)
d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
   parent pin (pin-id:2)

Where step c) is required due to AUTOMATIC mode, and step d) required due to
phy recovery clock pin being connected through the MUX type pin.

Thank you!
Arkadiusz

>
>Thanks!
>
>
>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-20 17:31       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-20 17:31 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, July 20, 2023 4:09 PM
>
>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>+/**
>>+ * 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
>>+ * @extack: error reporting
>>+ *
>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    enum ice_dpll_pin_type pin_type,
>>+		    struct netlink_ext_ack *extack)
>>+{
>>+	u8 flags = 0;
>>+	int ret;
>>+
>
>
>
>I don't follow. Howcome you don't check if the mode is freerun here or
>not? Is it valid to enable a pin when freerun mode? What happens?
>

Because you are probably still thinking the modes are somehow connected
to the state of the pin, but it is the other way around.
The dpll device mode is a state of DPLL before pins are even considered.
If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
any of the pins.

>Also, I am probably slow, but I still don't see anywhere in this
>patchset any description about why we need the freerun mode. What is
>diffrerent between:
>1) freerun mode
>2) automatic mode & all pins disabled?

The difference:
Case I:
1. set dpll to FREERUN and configure the source as if it would be in
AUTOMATIC
2. switch to AUTOMATIC
3. connecting to the valid source takes ~50 seconds

Case II:
1. set dpll to AUTOMATIC, set all the source to disconnected
2. switch one valid source to SELECTABLE
3. connecting to the valid source takes ~10 seconds

Basically in AUTOMATIC mode the sources are still monitored even when they
are not in SELECTABLE state, while in FREERUN there is no such monitoring,
so in the end process of synchronizing with the source takes much longer as
dpll need to start the process from scratch.

>
>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>needs to be documented, please.
>

Sure will add the description of FREERUN to the docs.

>
>
>Another question, I asked the last time as well, but was not heard:
>Consider example where you have 2 netdevices, eth0 and eth1, each
>connected with a single DPLL pin:
>eth0 - DPLL pin 10 (DPLL device id 2)
>eth1 - DPLL pin 11 (DPLL device id 2)
>
>You have a SyncE daemon running on top eth0 and eth1.
>
>Could you please describe following 2 flows?
>
>1) SyncE daemon selects eth0 as a source of clock
>2) SyncE daemon selects eth1 as a source of clock
>
>
>For mlx5 it goes like:
>
>DPLL device mode is MANUAL.
>1)
> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>    -> pin_id: 10
> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>    -> device_id: 2

Not sure if it needs to obtain the dpll id in this step, but it doesn't
relate to the dpll interface..

> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>CONNECTED
>
>2)
> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>    -> pin_id: 11
> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>    -> device_id: 2
> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>CONNECTED
> (that will in HW disconnect previously connected pin 10, there will be
>  notification of pin_id 10, device_id -> state DISCONNECT)
>

This flow is similar for ice, but there are some differences, although
they come from the fact, the ice is using AUTOMATIC mode and recovered
clock pins which are not directly connected to a dpll (connected through
the MUX pin).

1) 
a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 13
b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
   (in case of dpll_id is needed, would be find in this response also)
c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
   other pins shall be lower prio i.e. pin-prio:1)
d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
   parent pin (pin-id:2)
 
2) (basically the same, only eth1 would get different pin_id.)
a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 14
b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
   other pins shall be lower prio i.e. pin-prio:1)
d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
   parent pin (pin-id:2)

Where step c) is required due to AUTOMATIC mode, and step d) required due to
phy recovery clock pin being connected through the MUX type pin.

Thank you!
Arkadiusz

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20 17:31       ` Kubalewski, Arkadiusz
@ 2023-07-21  7:33         ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21  7:33 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, kuba
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, July 20, 2023 4:09 PM
>>
>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>[...]
>>
>>
>>>+/**
>>>+ * 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
>>>+ * @extack: error reporting
>>>+ *
>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>+ *
>>>+ * Context: Called under pf->dplls.lock
>>>+ * Return:
>>>+ * * 0 - OK
>>>+ * * negative - error
>>>+ */
>>>+static int
>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>+		    enum ice_dpll_pin_type pin_type,
>>>+		    struct netlink_ext_ack *extack)
>>>+{
>>>+	u8 flags = 0;
>>>+	int ret;
>>>+
>>
>>
>>
>>I don't follow. Howcome you don't check if the mode is freerun here or
>>not? Is it valid to enable a pin when freerun mode? What happens?
>>
>
>Because you are probably still thinking the modes are somehow connected
>to the state of the pin, but it is the other way around.
>The dpll device mode is a state of DPLL before pins are even considered.
>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>any of the pins.
>
>>Also, I am probably slow, but I still don't see anywhere in this
>>patchset any description about why we need the freerun mode. What is
>>diffrerent between:
>>1) freerun mode
>>2) automatic mode & all pins disabled?
>
>The difference:
>Case I:
>1. set dpll to FREERUN and configure the source as if it would be in
>AUTOMATIC
>2. switch to AUTOMATIC
>3. connecting to the valid source takes ~50 seconds
>
>Case II:
>1. set dpll to AUTOMATIC, set all the source to disconnected
>2. switch one valid source to SELECTABLE
>3. connecting to the valid source takes ~10 seconds
>
>Basically in AUTOMATIC mode the sources are still monitored even when they
>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>so in the end process of synchronizing with the source takes much longer as
>dpll need to start the process from scratch.

I believe this is implementation detail of your HW. How you do it is up
to you. User does not have any visibility to this behaviour, therefore
makes no sense to expose UAPI that is considering it. Please drop it at
least for the initial patchset version. If you really need it later on
(which I honestly doubt), you can send it as a follow-up patchset.



>
>>
>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>needs to be documented, please.
>>
>
>Sure will add the description of FREERUN to the docs.

No, please drop it from this patchset. I have no clue why you readded
it in the first place in the last patchset version.


>
>>
>>
>>Another question, I asked the last time as well, but was not heard:
>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>connected with a single DPLL pin:
>>eth0 - DPLL pin 10 (DPLL device id 2)
>>eth1 - DPLL pin 11 (DPLL device id 2)
>>
>>You have a SyncE daemon running on top eth0 and eth1.
>>
>>Could you please describe following 2 flows?
>>
>>1) SyncE daemon selects eth0 as a source of clock
>>2) SyncE daemon selects eth1 as a source of clock
>>
>>
>>For mlx5 it goes like:
>>
>>DPLL device mode is MANUAL.
>>1)
>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>    -> pin_id: 10
>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>    -> device_id: 2
>
>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>relate to the dpll interface..

Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
You need to set the state on a pin on a certain DPLL device.


>
>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>CONNECTED
>>
>>2)
>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>    -> pin_id: 11
>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>    -> device_id: 2
>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>CONNECTED
>> (that will in HW disconnect previously connected pin 10, there will be
>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>
>
>This flow is similar for ice, but there are some differences, although
>they come from the fact, the ice is using AUTOMATIC mode and recovered
>clock pins which are not directly connected to a dpll (connected through
>the MUX pin).
>
>1) 
>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 13
>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>   (in case of dpll_id is needed, would be find in this response also)
>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>   other pins shall be lower prio i.e. pin-prio:1)

Yeah, for this you need pin_id 2 and device_id. Because you are setting
state on DPLL device.


>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>   parent pin (pin-id:2)

For this you need pin_id and pin_parent_id because you set the state on
a parent pin.


Yeah, this is exactly why I initially was in favour of hiding all the
muxes and magic around it hidden from the user. Now every userspace app
working with this has to implement a logic of tracking pin and the mux
parents (possibly multiple levels) and configure everything. But it just
need a simple thing: "select this pin as a source" :/


Jakub, isn't this sort of unnecessary HW-details complexicity exposure
in UAPI you were against in the past? Am I missing something?



> 
>2) (basically the same, only eth1 would get different pin_id.)
>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 14
>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>   other pins shall be lower prio i.e. pin-prio:1)
>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>   parent pin (pin-id:2)
>
>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>phy recovery clock pin being connected through the MUX type pin.
>
>Thank you!
>Arkadiusz
>
>>
>>Thanks!
>>
>>
>>[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21  7:33         ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21  7:33 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, kuba
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, July 20, 2023 4:09 PM
>>
>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>[...]
>>
>>
>>>+/**
>>>+ * 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
>>>+ * @extack: error reporting
>>>+ *
>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>+ *
>>>+ * Context: Called under pf->dplls.lock
>>>+ * Return:
>>>+ * * 0 - OK
>>>+ * * negative - error
>>>+ */
>>>+static int
>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>+		    enum ice_dpll_pin_type pin_type,
>>>+		    struct netlink_ext_ack *extack)
>>>+{
>>>+	u8 flags = 0;
>>>+	int ret;
>>>+
>>
>>
>>
>>I don't follow. Howcome you don't check if the mode is freerun here or
>>not? Is it valid to enable a pin when freerun mode? What happens?
>>
>
>Because you are probably still thinking the modes are somehow connected
>to the state of the pin, but it is the other way around.
>The dpll device mode is a state of DPLL before pins are even considered.
>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>any of the pins.
>
>>Also, I am probably slow, but I still don't see anywhere in this
>>patchset any description about why we need the freerun mode. What is
>>diffrerent between:
>>1) freerun mode
>>2) automatic mode & all pins disabled?
>
>The difference:
>Case I:
>1. set dpll to FREERUN and configure the source as if it would be in
>AUTOMATIC
>2. switch to AUTOMATIC
>3. connecting to the valid source takes ~50 seconds
>
>Case II:
>1. set dpll to AUTOMATIC, set all the source to disconnected
>2. switch one valid source to SELECTABLE
>3. connecting to the valid source takes ~10 seconds
>
>Basically in AUTOMATIC mode the sources are still monitored even when they
>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>so in the end process of synchronizing with the source takes much longer as
>dpll need to start the process from scratch.

I believe this is implementation detail of your HW. How you do it is up
to you. User does not have any visibility to this behaviour, therefore
makes no sense to expose UAPI that is considering it. Please drop it at
least for the initial patchset version. If you really need it later on
(which I honestly doubt), you can send it as a follow-up patchset.



>
>>
>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>needs to be documented, please.
>>
>
>Sure will add the description of FREERUN to the docs.

No, please drop it from this patchset. I have no clue why you readded
it in the first place in the last patchset version.


>
>>
>>
>>Another question, I asked the last time as well, but was not heard:
>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>connected with a single DPLL pin:
>>eth0 - DPLL pin 10 (DPLL device id 2)
>>eth1 - DPLL pin 11 (DPLL device id 2)
>>
>>You have a SyncE daemon running on top eth0 and eth1.
>>
>>Could you please describe following 2 flows?
>>
>>1) SyncE daemon selects eth0 as a source of clock
>>2) SyncE daemon selects eth1 as a source of clock
>>
>>
>>For mlx5 it goes like:
>>
>>DPLL device mode is MANUAL.
>>1)
>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>    -> pin_id: 10
>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>    -> device_id: 2
>
>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>relate to the dpll interface..

Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
You need to set the state on a pin on a certain DPLL device.


>
>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>CONNECTED
>>
>>2)
>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>    -> pin_id: 11
>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>    -> device_id: 2
>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>CONNECTED
>> (that will in HW disconnect previously connected pin 10, there will be
>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>
>
>This flow is similar for ice, but there are some differences, although
>they come from the fact, the ice is using AUTOMATIC mode and recovered
>clock pins which are not directly connected to a dpll (connected through
>the MUX pin).
>
>1) 
>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 13
>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>   (in case of dpll_id is needed, would be find in this response also)
>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>   other pins shall be lower prio i.e. pin-prio:1)

Yeah, for this you need pin_id 2 and device_id. Because you are setting
state on DPLL device.


>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>   parent pin (pin-id:2)

For this you need pin_id and pin_parent_id because you set the state on
a parent pin.


Yeah, this is exactly why I initially was in favour of hiding all the
muxes and magic around it hidden from the user. Now every userspace app
working with this has to implement a logic of tracking pin and the mux
parents (possibly multiple levels) and configure everything. But it just
need a simple thing: "select this pin as a source" :/


Jakub, isn't this sort of unnecessary HW-details complexicity exposure
in UAPI you were against in the past? Am I missing something?



> 
>2) (basically the same, only eth1 would get different pin_id.)
>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 -> pin_id: 14
>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>   other pins shall be lower prio i.e. pin-prio:1)
>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>   parent pin (pin-id:2)
>
>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>phy recovery clock pin being connected through the MUX type pin.
>
>Thank you!
>Arkadiusz
>
>>
>>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] 109+ messages in thread

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-21  7:48   ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21  7:48 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev wrote:

>v8 RFC -> v0:

Just to avoid future confusion: In netdev, the count goes from 1.
So this is v1. Next submission will be v2.

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

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-21  7:48   ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21  7:48 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev wrote:

>v8 RFC -> v0:

Just to avoid future confusion: In netdev, the count goes from 1.
So this is v1. Next submission will be v2.

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

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
  2023-07-20  9:18 ` Vadim Fedorenko
@ 2023-07-21 11:14   ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 11:14 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

There are couple of issues that came up during our internal ci run:

10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]

10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement

I believe that you didn't run make with C=2, otherwise you would hit
these.

Checkpatch issue:
10:29:30  CHECK: struct mutex definition without comment
10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
10:29:30  +	struct mutex lock;

Spelling errors:
10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties


Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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

s/API aim/API aims/


>make it flexible and easy to cover special configurations.

I don't follow. How this could "aim to extend current pin configuration" ?
This is a new thing. Could you re-phrase?

What's "special configuration"? Sounds odd.


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

"$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
root is needed to perform the command.


>--dump device-get
>[{'clock-id': 282574471561216,
>  'id': 0,
>  'lock-status': 'unlocked',
>  'mode': 'automatic',
>  'module-name': 'ice',
>  'type': 'eec'},
> {'clock-id': 282574471561216,
>  'id': 1,
>  'lock-status': 'unlocked',
>  'mode': 'automatic',
>  'module-name': 'ice',
>  'type': 'pps'}]
>
>- get single pin info:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-get --json '{"pin-id":2}'
>{'clock-id': 282574471561216,
> 'module-name': 'ice',
> 'pin-board-label': 'C827_0-RCLKA',
> 'pin-dpll-caps': 6,
> 'pin-frequency': 1953125,
> 'pin-id': 2,
> 'pin-parenti-device': [{'id': 0,

This looks like manual edit went wrong :)
s/parenti/parent/


>                         'pin-direction': 'input',
>                         'pin-prio': 11,
>                         'pin-state': 'selectable'},
>                        {'id': 1,
>                         'pin-direction': 'input',
>                         'pin-prio': 9,
>                         'pin-state': 'selectable'}],
> 'pin-type': 'mux'}
>
>- set pin's state on dpll:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>
>- set pin's prio on dpll:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>
>- set pin's state on parent pin:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":13, \
>                      "pin-parent-pin":{"pin-id":2, "pin-state":1}}'
>

[...]

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

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-21 11:14   ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 11:14 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

There are couple of issues that came up during our internal ci run:

10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]

10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement

I believe that you didn't run make with C=2, otherwise you would hit
these.

Checkpatch issue:
10:29:30  CHECK: struct mutex definition without comment
10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
10:29:30  +	struct mutex lock;

Spelling errors:
10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties


Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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

s/API aim/API aims/


>make it flexible and easy to cover special configurations.

I don't follow. How this could "aim to extend current pin configuration" ?
This is a new thing. Could you re-phrase?

What's "special configuration"? Sounds odd.


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

"$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
root is needed to perform the command.


>--dump device-get
>[{'clock-id': 282574471561216,
>  'id': 0,
>  'lock-status': 'unlocked',
>  'mode': 'automatic',
>  'module-name': 'ice',
>  'type': 'eec'},
> {'clock-id': 282574471561216,
>  'id': 1,
>  'lock-status': 'unlocked',
>  'mode': 'automatic',
>  'module-name': 'ice',
>  'type': 'pps'}]
>
>- get single pin info:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-get --json '{"pin-id":2}'
>{'clock-id': 282574471561216,
> 'module-name': 'ice',
> 'pin-board-label': 'C827_0-RCLKA',
> 'pin-dpll-caps': 6,
> 'pin-frequency': 1953125,
> 'pin-id': 2,
> 'pin-parenti-device': [{'id': 0,

This looks like manual edit went wrong :)
s/parenti/parent/


>                         'pin-direction': 'input',
>                         'pin-prio': 11,
>                         'pin-state': 'selectable'},
>                        {'id': 1,
>                         'pin-direction': 'input',
>                         'pin-prio': 9,
>                         'pin-state': 'selectable'}],
> 'pin-type': 'mux'}
>
>- set pin's state on dpll:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>
>- set pin's prio on dpll:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>
>- set pin's state on parent pin:
>$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>--do pin-set --json '{"pin-id":13, \
>                      "pin-parent-pin":{"pin-id":2, "pin-state":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] 109+ messages in thread

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21  7:33         ` Jiri Pirko
@ 2023-07-21 11:17           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 11:17 UTC (permalink / raw)
  To: Jiri Pirko, kuba
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 9:33 AM
>
>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>
>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>>[...]
>>>
>>>
>>>>+/**
>>>>+ * 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
>>>>+ * @extack: error reporting
>>>>+ *
>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>+ *
>>>>+ * Context: Called under pf->dplls.lock
>>>>+ * Return:
>>>>+ * * 0 - OK
>>>>+ * * negative - error
>>>>+ */
>>>>+static int
>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>+		    struct netlink_ext_ack *extack)
>>>>+{
>>>>+	u8 flags = 0;
>>>>+	int ret;
>>>>+
>>>
>>>
>>>
>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>
>>
>>Because you are probably still thinking the modes are somehow connected
>>to the state of the pin, but it is the other way around.
>>The dpll device mode is a state of DPLL before pins are even considered.
>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>any of the pins.
>>
>>>Also, I am probably slow, but I still don't see anywhere in this
>>>patchset any description about why we need the freerun mode. What is
>>>diffrerent between:
>>>1) freerun mode
>>>2) automatic mode & all pins disabled?
>>
>>The difference:
>>Case I:
>>1. set dpll to FREERUN and configure the source as if it would be in
>>AUTOMATIC
>>2. switch to AUTOMATIC
>>3. connecting to the valid source takes ~50 seconds
>>
>>Case II:
>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>2. switch one valid source to SELECTABLE
>>3. connecting to the valid source takes ~10 seconds
>>
>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>so in the end process of synchronizing with the source takes much longer as
>>dpll need to start the process from scratch.
>
>I believe this is implementation detail of your HW. How you do it is up
>to you. User does not have any visibility to this behaviour, therefore
>makes no sense to expose UAPI that is considering it. Please drop it at
>least for the initial patchset version. If you really need it later on
>(which I honestly doubt), you can send it as a follow-up patchset.
>

And we will have the same discussion later.. But implementation is already
there.
As said in our previous discussion, without mode_set there is no point to have
command DEVICE_SET at all, and there you said that you are ok with having the
command as a placeholder, which doesn't make sense, since it is not used. 

Also this is not HW implementation detail but a synchronizer chip feature,
once dpll is in FREERUN mode, the measurements like phase offset between the
input and dpll's output won't be available.

For the user there is a difference..
Enabling the FREERUN mode is a reset button on the dpll's state machine,
where disconnecting sources is not, as they are still used, monitored and
measured.
So probably most important fact that you are missing here: assuming the user
disconnects the pin that dpll was locked with, our dpll doesn't go into UNLOCKED
state but into HOLDOVER.

>
>
>>
>>>
>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>needs to be documented, please.
>>>
>>
>>Sure will add the description of FREERUN to the docs.
>
>No, please drop it from this patchset. I have no clue why you readded
>it in the first place in the last patchset version.
>

mode_set was there from the very beginning.. now implemented in ice driver
as it should.

>
>>
>>>
>>>
>>>Another question, I asked the last time as well, but was not heard:
>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>connected with a single DPLL pin:
>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>
>>>You have a SyncE daemon running on top eth0 and eth1.
>>>
>>>Could you please describe following 2 flows?
>>>
>>>1) SyncE daemon selects eth0 as a source of clock
>>>2) SyncE daemon selects eth1 as a source of clock
>>>
>>>
>>>For mlx5 it goes like:
>>>
>>>DPLL device mode is MANUAL.
>>>1)
>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>    -> pin_id: 10
>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>    -> device_id: 2
>>
>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>relate to the dpll interface..
>
>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>You need to set the state on a pin on a certain DPLL device.
>

The thing is pin can be connected to multiple dplls and SyncE daemon shall
know already something about the dpll it is managing.
Not saying it is not needed, I am saying this is not a moment the SyncE daemon
learns it.
But let's park it, as this is not really relevant.

>
>>
>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>CONNECTED
>>>
>>>2)
>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>    -> pin_id: 11
>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>    -> device_id: 2
>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>CONNECTED
>>> (that will in HW disconnect previously connected pin 10, there will be
>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>
>>
>>This flow is similar for ice, but there are some differences, although
>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>clock pins which are not directly connected to a dpll (connected through
>>the MUX pin).
>>
>>1)
>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>pin_id: 13
>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>   (in case of dpll_id is needed, would be find in this response also)
>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>   other pins shall be lower prio i.e. pin-prio:1)
>
>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>state on DPLL device.
>
>
>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>   parent pin (pin-id:2)
>
>For this you need pin_id and pin_parent_id because you set the state on
>a parent pin.
>
>
>Yeah, this is exactly why I initially was in favour of hiding all the
>muxes and magic around it hidden from the user. Now every userspace app
>working with this has to implement a logic of tracking pin and the mux
>parents (possibly multiple levels) and configure everything. But it just
>need a simple thing: "select this pin as a source" :/
>
>
>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>in UAPI you were against in the past? Am I missing something?
>

Multiple level of muxes possibly could be hidden in the driver, but the fact
they exist is not possible to be hidden from the user if the DPLL is in
AUTOMATIC mode.
For MANUAL mode dpll the muxes could be also hidden.
Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
pin.

Thank you!
Arkadiusz

>
>
>>
>>2) (basically the same, only eth1 would get different pin_id.)
>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>pin_id: 14
>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>   other pins shall be lower prio i.e. pin-prio:1)
>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>   parent pin (pin-id:2)
>>
>>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>>phy recovery clock pin being connected through the MUX type pin.
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>Thanks!
>>>
>>>
>>>[...]


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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 11:17           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 11:17 UTC (permalink / raw)
  To: Jiri Pirko, kuba
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 9:33 AM
>
>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>
>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>>[...]
>>>
>>>
>>>>+/**
>>>>+ * 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
>>>>+ * @extack: error reporting
>>>>+ *
>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>+ *
>>>>+ * Context: Called under pf->dplls.lock
>>>>+ * Return:
>>>>+ * * 0 - OK
>>>>+ * * negative - error
>>>>+ */
>>>>+static int
>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>+		    struct netlink_ext_ack *extack)
>>>>+{
>>>>+	u8 flags = 0;
>>>>+	int ret;
>>>>+
>>>
>>>
>>>
>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>
>>
>>Because you are probably still thinking the modes are somehow connected
>>to the state of the pin, but it is the other way around.
>>The dpll device mode is a state of DPLL before pins are even considered.
>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>any of the pins.
>>
>>>Also, I am probably slow, but I still don't see anywhere in this
>>>patchset any description about why we need the freerun mode. What is
>>>diffrerent between:
>>>1) freerun mode
>>>2) automatic mode & all pins disabled?
>>
>>The difference:
>>Case I:
>>1. set dpll to FREERUN and configure the source as if it would be in
>>AUTOMATIC
>>2. switch to AUTOMATIC
>>3. connecting to the valid source takes ~50 seconds
>>
>>Case II:
>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>2. switch one valid source to SELECTABLE
>>3. connecting to the valid source takes ~10 seconds
>>
>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>so in the end process of synchronizing with the source takes much longer as
>>dpll need to start the process from scratch.
>
>I believe this is implementation detail of your HW. How you do it is up
>to you. User does not have any visibility to this behaviour, therefore
>makes no sense to expose UAPI that is considering it. Please drop it at
>least for the initial patchset version. If you really need it later on
>(which I honestly doubt), you can send it as a follow-up patchset.
>

And we will have the same discussion later.. But implementation is already
there.
As said in our previous discussion, without mode_set there is no point to have
command DEVICE_SET at all, and there you said that you are ok with having the
command as a placeholder, which doesn't make sense, since it is not used. 

Also this is not HW implementation detail but a synchronizer chip feature,
once dpll is in FREERUN mode, the measurements like phase offset between the
input and dpll's output won't be available.

For the user there is a difference..
Enabling the FREERUN mode is a reset button on the dpll's state machine,
where disconnecting sources is not, as they are still used, monitored and
measured.
So probably most important fact that you are missing here: assuming the user
disconnects the pin that dpll was locked with, our dpll doesn't go into UNLOCKED
state but into HOLDOVER.

>
>
>>
>>>
>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>needs to be documented, please.
>>>
>>
>>Sure will add the description of FREERUN to the docs.
>
>No, please drop it from this patchset. I have no clue why you readded
>it in the first place in the last patchset version.
>

mode_set was there from the very beginning.. now implemented in ice driver
as it should.

>
>>
>>>
>>>
>>>Another question, I asked the last time as well, but was not heard:
>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>connected with a single DPLL pin:
>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>
>>>You have a SyncE daemon running on top eth0 and eth1.
>>>
>>>Could you please describe following 2 flows?
>>>
>>>1) SyncE daemon selects eth0 as a source of clock
>>>2) SyncE daemon selects eth1 as a source of clock
>>>
>>>
>>>For mlx5 it goes like:
>>>
>>>DPLL device mode is MANUAL.
>>>1)
>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>    -> pin_id: 10
>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>    -> device_id: 2
>>
>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>relate to the dpll interface..
>
>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>You need to set the state on a pin on a certain DPLL device.
>

The thing is pin can be connected to multiple dplls and SyncE daemon shall
know already something about the dpll it is managing.
Not saying it is not needed, I am saying this is not a moment the SyncE daemon
learns it.
But let's park it, as this is not really relevant.

>
>>
>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>CONNECTED
>>>
>>>2)
>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>    -> pin_id: 11
>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>    -> device_id: 2
>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>CONNECTED
>>> (that will in HW disconnect previously connected pin 10, there will be
>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>
>>
>>This flow is similar for ice, but there are some differences, although
>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>clock pins which are not directly connected to a dpll (connected through
>>the MUX pin).
>>
>>1)
>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>pin_id: 13
>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>   (in case of dpll_id is needed, would be find in this response also)
>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>   other pins shall be lower prio i.e. pin-prio:1)
>
>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>state on DPLL device.
>
>
>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>   parent pin (pin-id:2)
>
>For this you need pin_id and pin_parent_id because you set the state on
>a parent pin.
>
>
>Yeah, this is exactly why I initially was in favour of hiding all the
>muxes and magic around it hidden from the user. Now every userspace app
>working with this has to implement a logic of tracking pin and the mux
>parents (possibly multiple levels) and configure everything. But it just
>need a simple thing: "select this pin as a source" :/
>
>
>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>in UAPI you were against in the past? Am I missing something?
>

Multiple level of muxes possibly could be hidden in the driver, but the fact
they exist is not possible to be hidden from the user if the DPLL is in
AUTOMATIC mode.
For MANUAL mode dpll the muxes could be also hidden.
Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
pin.

Thank you!
Arkadiusz

>
>
>>
>>2) (basically the same, only eth1 would get different pin_id.)
>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>pin_id: 14
>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>   other pins shall be lower prio i.e. pin-prio:1)
>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>   parent pin (pin-id:2)
>>
>>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>>phy recovery clock pin being connected through the MUX type pin.
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>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] 109+ messages in thread

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20  9:19   ` Vadim Fedorenko
@ 2023-07-21 11:39     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 11:39 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Control over clock generation unit is required for further development
>of Synchronous Ethernet feature. Interface provides ability to obtain
>current state of a dpll, its sources and outputs which are pins, and
>allows their configuration.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
>RFC v9->v0:
>- add support for DPLL_MODE_FREERUN mode
>- use DPLL_LOCK_STATUS, remove ICE_DPLL_LOCK_STATUS
>- fix mutex locking scheme
>- remove rclk pin label
>- fix/remmove struct fields descriptions
>
>v8->v9:
>- drop pointless 0 assignement
>- ice_dpll_init(..) returns void instead of int
>- fix context description of the functions
>- fix ice_dpll_init(..) traces
>- fix use package_label instead pf board_label for rclk pin
>- be consistent on cgu presence naming
>- remove indent in ice_dpll_deinit(..)
>- remove unused struct field lock_err_num
>- fix kworker resched behavior
>- remove debug log from ice_dpll_deinit_worker(..)
>- reorder ice internal functions
>- release resources directly on error path
>- remove redundant NULL checks when releasing resources
>- do not assign NULL to pointers after releasing resources
>- simplify variable assignement
>- fix 'int ret;' declarations across the ice_dpll.c
>- remove leftover ice_dpll_find(..)
>- get pf pointer from dpll_priv without type cast
>- improve error reporting
>- fix documentation
>- fix ice_dpll_update_state(..) flow
>- fix return in case out of range prio set
>
> drivers/net/ethernet/intel/Kconfig        |    1 +
> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
> drivers/net/ethernet/intel/ice/ice.h      |    3 +
> drivers/net/ethernet/intel/ice/ice_dpll.c | 2053 +++++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h |  104 ++
> drivers/net/ethernet/intel/ice/ice_main.c |    7 +
> 6 files changed, 2170 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>
>diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
>index 9bc0a9519899..913dcf928d15 100644
>--- a/drivers/net/ethernet/intel/Kconfig
>+++ b/drivers/net/ethernet/intel/Kconfig
>@@ -284,6 +284,7 @@ config ICE
> 	select DIMLIB
> 	select NET_DEVLINK
> 	select PLDMFW
>+	select DPLL
> 	help
> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
> 	  devices.  For more information on how to identify your adapter, go
>diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
>index 817977e3039d..85d6366d1f5b 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -34,7 +34,8 @@ ice-y := ice_main.o	\
> 	 ice_lag.o	\
> 	 ice_ethtool.o  \
> 	 ice_repr.o	\
>-	 ice_tc_lib.o
>+	 ice_tc_lib.o	\
>+	 ice_dpll.o
> ice-$(CONFIG_PCI_IOV) +=	\
> 	ice_sriov.o		\
> 	ice_virtchnl.o		\
>diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
>index 484d1d143174..a520141ef665 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -76,6 +76,7 @@
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
> #include "ice_irq.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -507,6 +508,7 @@ enum ice_pf_flags {
> 	ICE_FLAG_UNPLUG_AUX_DEV,
> 	ICE_FLAG_MTU_CHANGED,
> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
> 	ICE_PF_FLAGS_NBITS		/* must be last */
> };
> 
>@@ -636,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..ba319cfb9167
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,2053 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#include "ice.h"
>+#include "ice_lib.h"
>+#include "ice_trace.h"
>+#include <linux/dpll.h>
>+
>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
>+#define ICE_DPLL_LOCK_TRIES			1000
>+#define ICE_DPLL_PIN_IDX_INVALID		0xff
>+#define ICE_DPLL_RCLK_NUM_PER_PF		1
>+
>+/**
>+ * enum ice_dpll_pin_type - enumerate ice pin types:
>+ * @ICE_DPLL_PIN_INVALID: invalid pin type
>+ * @ICE_DPLL_PIN_TYPE_INPUT: input pin
>+ * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
>+ * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
>+ */
>+enum ice_dpll_pin_type {
>+	ICE_DPLL_PIN_INVALID,
>+	ICE_DPLL_PIN_TYPE_INPUT,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>+};
>+
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_INPUT] = "input",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
>+};
>+
>+/**
>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>+ * @pf: private board structure
>+ * @extack: error reporting
>+ *
>+ * 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.

I don't know, I will probably need to beg you here. Why exactly are you
ignoring my comments? It's not nice, I thought we are way past it...

There is no "dead lock". Could you please describe how exactly
the case you mention can happen? It can't.
Could you please remove the trylock iteration below?
It's completely pointless.



>+ *
>+ * Return:
>+ * 0 - if lock acquired
>+ * negative - lock not acquired or dpll is not initialized
>+ */
>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack *extack)
>+{
>+	int i;
>+
>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {

And again, as I already told you, this flag checking is totally
pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().



>+			if (extack)
>+				NL_SET_ERR_MSG(extack,
>+					       "ice dpll not initialized");
>+			return -EFAULT;
>+		}
>+		if (mutex_trylock(&pf->dplls.lock))
>+			return 0;
>+		usleep_range(100, 150);
>+	}
>+	if (extack)

No need to check for NULL, but this code should be dropped anyway.

>+		NL_SET_ERR_MSG(extack, "was not able to acquire mutex");
>+
>+	return -EBUSY;
>+}
>+
>+/**
>+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>+ * @pf: private board structure
>+ *
>+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
>+ */
>+static void ice_dpll_cb_unlock(struct ice_pf *pf)
>+{
>+	mutex_unlock(&pf->dplls.lock);
>+}
>+
>+/**
>+ * ice_dpll_pin_freq_set - set pin's frequency
>+ * @pf: private board structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being configured
>+ * @freq: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Set requested frequency on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error on AQ or wrong pin type given
>+ */
>+static int
>+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+		      enum ice_dpll_pin_type pin_type, const u32 freq,
>+		      struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+	u8 flags;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>+					       pin->flags[0], freq, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret) {
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   freq, pin->idx);
>+		return ret;
>+	}
>+	pin->freq = freq;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       const u32 frequency,
>+		       struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_set - input pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+			     const struct dpll_device *dpll, void *dpll_priv,
>+			     u64 frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_set - output pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+			      const struct dpll_device *dpll, void *dpll_priv,
>+			      u64 frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       u64 *frequency, struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*frequency = p->freq;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_get - input pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a input pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+			     const struct dpll_device *dpll, void *dpll_priv,
>+			     u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_get - output pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+			      const struct dpll_device *dpll, void *dpll_priv,
>+			      u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_enable - enable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being enabled
>+ * @extack: error reporting
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    enum ice_dpll_pin_type pin_type,
>+		    struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to enable %s pin:%u\n",
>+				   ret, ice_aq_str(hw->adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_disable - disable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being disabled
>+ * @extack: error reporting
>+ *
>+ * Disable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		     enum ice_dpll_pin_type pin_type,
>+		     struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to disable %s pin:%u\n",
>+				   ret, ice_aq_str(hw->adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_update - update pin's state
>+ * @pf: private board struct
>+ * @pin: structure with pin attributes to be updated
>+ * @pin_type: type of pin being updated
>+ * @extack: error reporting
>+ *
>+ * Determine pin current state and frequency, then update struct
>+ * holding the pin info. For input pin states are separated for each
>+ * dpll, for rclk pins states are separated for each parent.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			  enum ice_dpll_pin_type pin_type,
>+			  struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>+					       NULL, &pin->flags[0],
>+					       &pin->freq, NULL);
>+		if (ret)
>+			goto err;
>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>+			if (pin->pin) {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					pin->pin == pf->dplls.eec.active_input ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					pin->pin == pf->dplls.pps.active_input ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+			} else {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					DPLL_PIN_STATE_SELECTABLE;
>+			}
>+		} else {
>+			pin->state[pf->dplls.eec.dpll_idx] =
>+				DPLL_PIN_STATE_DISCONNECTED;
>+			pin->state[pf->dplls.pps.dpll_idx] =
>+				DPLL_PIN_STATE_DISCONNECTED;
>+		}
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>+						&pin->flags[0], NULL,
>+						&pin->freq, NULL);
>+		if (ret)
>+			goto err;
>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>+		else
>+			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>+		break;
>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>+
>+		for (parent = 0; parent < pf->dplls.rclk.num_parents;
>+		     parent++) {
>+			u8 p = parent;
>+
>+			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
>+							 &port_num,
>+							 &pin->flags[parent],
>+							 NULL);
>+			if (ret)
>+				goto err;
>+			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>+			    pin->flags[parent])
>+				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>+			else
>+				pin->state[parent] =
>+					DPLL_PIN_STATE_DISCONNECTED;
>+		}
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+
>+	return 0;
>+err:
>+	if (extack)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to update %s pin:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+	else
>+		dev_err_ratelimited(ice_pf_to_dev(pf),
>+				    "err:%d %s failed to update %s pin:%u\n",
>+				    ret,
>+				    ice_aq_str(pf->hw.adminq.sq_last_status),
>+				    pin_type_name[pin_type], pin->idx);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
>+ * @pf: board private structure
>+ * @dpll: ice dpll pointer
>+ * @pin: ice pin pointer
>+ * @prio: priority value being set on a dpll
>+ * @extack: error reporting
>+ *
>+ * Internal wrapper for setting the priority in the hardware.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>+			   struct ice_dpll_pin *pin, const u32 prio,
>+			   struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+
>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>+				      (u8)prio);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "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
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @status: on success holds dpll's lock status
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback, provides dpll's lock status.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_lock_status *status,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*status = d->dpll_state;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_supported - check if dpll's working mode is supported
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: mode to be checked for support
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides information if working mode is supported
>+ * by dpll.
>+ *
>+ * Return:
>+ * * true - mode is supported
>+ * * false - mode is not supported
>+ */
>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>+				    void *dpll_priv,
>+				    enum dpll_mode mode,
>+				    struct netlink_ext_ack *extack)
>+{
>+	if (mode == DPLL_MODE_AUTOMATIC ||
>+	    mode == DPLL_MODE_FREERUN)
>+		return true;
>+
>+	return false;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - get dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: on success holds current working mode of dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
>+			     enum dpll_mode *mode,
>+			     struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*mode = d->mode;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_mode_set - set dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: requested working mode of dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. User requests working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
>+			     enum dpll_mode mode,
>+			     struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	u8 config;
>+	int ret;
>+
>+	switch (mode) {
>+	case DPLL_MODE_AUTOMATIC:
>+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC;
>+		break;
>+	case DPLL_MODE_FREERUN:
>+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_aq_set_cgu_dpll_config(&pf->hw, d->dpll_idx, d->ref_state,
>+					 config, d->eec_mode);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set mode:%u on dpll:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   mode, d->dpll_idx);
>+	else
>+		d->mode = mode;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_set - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @enable: if pin shalll be enabled
>+ * @extack: error reporting
>+ * @pin_type: type of a pin
>+ *
>+ * Set pin state on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       bool enable, struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	if (enable)
>+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type, extack);
>+	else
>+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type, extack);
>+	if (!ret)
>+		ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Set given state on output type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_state state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
>+
>+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Enables given mode on input type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_state state,
>+			 struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_SELECTABLE;
>+
>+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_state_get - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ * @pin_type: type of questioned pin
>+ *
>+ * Determine pin state set it on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       enum dpll_pin_state *state,
>+		       struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
>+	if (ret)
>+		goto unlock;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
>+		*state = p->state[d->dpll_idx];
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>+		*state = p->state[0];
>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_get - get output pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_state *state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_get - get input pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_state *state,
>+			 struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_prio_get - get dpll's input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: on success - returns input priority on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
>+			const struct dpll_device *dpll, void *dpll_priv,
>+			u32 *prio, struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*prio = d->input_prio[p->idx];
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_prio_set - set dpll input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: input priority to be set on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
>+			const struct dpll_device *dpll, void *dpll_priv,
>+			u32 prio, struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX) {
>+		NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
>+				   ICE_DPLL_PRIO_MAX);
>+		return -EINVAL;
>+	}
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_direction - callback for get input pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds input pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of a input pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_direction *direction,
>+			 struct netlink_ext_ack *extack)
>+{
>+	*direction = DPLL_PIN_DIRECTION_INPUT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_output_direction - callback for get output pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds output pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of an output pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_direction *direction,
>+			  struct netlink_ext_ack *extack)
>+{
>+	*direction = DPLL_PIN_DIRECTION_OUTPUT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @parent_pin_priv: parent private data pointer passed on pin registration
>+ * @state: state to be set on pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
>+			       const struct dpll_pin *parent_pin,
>+			       void *parent_pin_priv,
>+			       enum dpll_pin_state state,
>+			       struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
>+	struct ice_pf *pf = p->pf;
>+	u32 hw_idx;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+	if (hw_idx >= pf->dplls.num_inputs)
>+		goto unlock;
>+
>+	if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
>+	    (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "pin:%u state:%u on parent:%u already set",
>+				   p->idx, state, parent->idx);
>+		ret = -EINVAL;
>+		goto unlock;
>+	}
>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
>+					 &p->freq);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set pin state:%u for pin:%u on parent:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   state, p->idx, parent->idx);
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @parent_pin_priv: pin parent priv data pointer passed on pin registration
>+ * @state: on success holds pin state on parent pin
>+ * @extack: error reporting
>+ *
>+ * dpll subsystem callback, get a state of a recovered clock pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
>+			       const struct dpll_pin *parent_pin,
>+			       void *parent_pin_priv,
>+			       enum dpll_pin_state *state,
>+			       struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+	struct ice_pf *pf = p->pf;
>+	u32 hw_idx;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+	if (hw_idx >= pf->dplls.num_inputs)
>+		goto unlock;
>+
>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>+					extack);
>+	if (ret)
>+		goto unlock;
>+
>+	*state = p->state[hw_idx];
>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
>+	.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
>+	.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
>+	.direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_input_ops = {
>+	.frequency_get = ice_dpll_input_frequency_get,
>+	.frequency_set = ice_dpll_input_frequency_set,
>+	.state_on_dpll_get = ice_dpll_input_state_get,
>+	.state_on_dpll_set = ice_dpll_input_state_set,
>+	.prio_get = ice_dpll_input_prio_get,
>+	.prio_set = ice_dpll_input_prio_set,
>+	.direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_output_ops = {
>+	.frequency_get = ice_dpll_output_frequency_get,
>+	.frequency_set = ice_dpll_output_frequency_set,
>+	.state_on_dpll_get = ice_dpll_output_state_get,
>+	.state_on_dpll_set = ice_dpll_output_state_set,
>+	.direction_get = ice_dpll_output_direction,
>+};
>+
>+static const struct dpll_device_ops ice_dpll_ops = {
>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_set = ice_dpll_mode_set,
>+};
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ *
>+ * Return: generated clock id for the board
>+ */
>+static u64 ice_generate_clock_id(struct ice_pf *pf)
>+{
>+	return pci_get_dsn(pf->pdev);
>+}
>+
>+/**
>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>+ * @d: pointer do dpll
>+ *
>+ * Once change detected appropriate event is submitted to the dpll subsystem.
>+ */
>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>+{
>+	if (d->prev_dpll_state != d->dpll_state) {
>+		d->prev_dpll_state = d->dpll_state;
>+		dpll_device_change_ntf(d->dpll);
>+	}
>+	if (d->prev_input != d->active_input) {
>+		if (d->prev_input)
>+			dpll_pin_change_ntf(d->prev_input);
>+		d->prev_input = d->active_input;
>+		if (d->active_input)
>+			dpll_pin_change_ntf(d->active_input);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_update_state - update dpll state
>+ * @pf: pf private structure
>+ * @d: pointer to queried dpll device
>+ * @init: if function called on initialization of ice dpll
>+ *
>+ * Poll current state of dpll from hw and update ice_dpll struct.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - AQ failure
>+ */
>+static int
>+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>+{
>+	struct ice_dpll_pin *p = NULL;
>+	int ret;
>+
>+	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>+				&d->input_idx, &d->ref_state, &d->eec_mode,
>+				&d->phase_shift, &d->dpll_state, &d->mode);
>+
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
>+		d->dpll_idx, d->prev_input_idx, d->input_idx,
>+		d->dpll_state, d->prev_dpll_state, d->mode);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"update dpll=%d state failed, ret=%d %s\n",
>+			d->dpll_idx, ret,
>+			ice_aq_str(pf->hw.adminq.sq_last_status));
>+		return ret;
>+	}
>+	if (init) {
>+		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
>+		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
>+			d->active_input = pf->dplls.inputs[d->input_idx].pin;
>+		p = &pf->dplls.inputs[d->input_idx];
>+		return ice_dpll_pin_state_update(pf, p,
>+						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
>+	}
>+	if (d->dpll_state == DPLL_LOCK_STATUS_HOLDOVER ||
>+	    d->dpll_state == DPLL_LOCK_STATUS_UNLOCKED) {
>+		d->active_input = NULL;
>+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID)
>+			p = &pf->dplls.inputs[d->input_idx];
>+		d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		if (!p)
>+			return 0;
>+		ret = ice_dpll_pin_state_update(pf, p,
>+						ICE_DPLL_PIN_TYPE_INPUT, NULL);
>+	} else if (d->input_idx != d->prev_input_idx) {
>+		if (d->prev_input_idx != ICE_DPLL_PIN_IDX_INVALID) {
>+			p = &pf->dplls.inputs[d->prev_input_idx];
>+			ice_dpll_pin_state_update(pf, p,
>+						  ICE_DPLL_PIN_TYPE_INPUT,
>+						  NULL);
>+		}
>+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID) {
>+			p = &pf->dplls.inputs[d->input_idx];
>+			d->active_input = p->pin;
>+			ice_dpll_pin_state_update(pf, p,
>+						  ICE_DPLL_PIN_TYPE_INPUT,
>+						  NULL);
>+		}
>+		d->prev_input_idx = d->input_idx;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ * Context: Holds pf->dplls.lock
>+ */
>+static void ice_dpll_periodic_work(struct kthread_work *work)
>+{
>+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, NULL);
>+	if (ret == -EBUSY)
>+		goto resched;
>+	else if (ret)
>+		return;
>+	ret = ice_dpll_update_state(pf, de, false);
>+	if (!ret)
>+		ret = ice_dpll_update_state(pf, dp, false);
>+	if (ret) {
>+		d->cgu_state_acq_err_num++;
>+		/* stop rescheduling this worker */
>+		if (d->cgu_state_acq_err_num >
>+		    ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
>+			dev_err(ice_pf_to_dev(pf),
>+				"EEC/PPS DPLLs periodic work disabled\n");
>+			return;
>+		}
>+	}
>+	ice_dpll_cb_unlock(pf);
>+	ice_dpll_notify_changes(de);
>+	ice_dpll_notify_changes(dp);
>+
>+resched:
>+	/* Run twice a second or reschedule if update failed */
>+	kthread_queue_delayed_work(d->kworker, &d->work,
>+				   ret ? msecs_to_jiffies(10) :
>+				   msecs_to_jiffies(500));
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ *
>+ * Release resources of given pins array in the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>+{
>+	int i;
>+
>+	for (i = 0; i < count; i++)
>+		dpll_pin_put(pins[i].pin);
>+}
>+
>+/**
>+ * ice_dpll_get_pins - get pins from dpll subsystem
>+ * @pf: board private structure
>+ * @pins: pointer to pins array
>+ * @start_idx: get starts from this pin idx value
>+ * @count: number of pins
>+ * @clock_id: clock_id of dpll device
>+ *
>+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
>+ * pins array.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation failure reason
>+ */
>+static int
>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>+		  int start_idx, int count, u64 clock_id)
>+{
>+	int i, ret;
>+
>+	for (i = 0; i < count; i++) {
>+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
>+					   &pins[i].prop);
>+		if (IS_ERR(pins[i].pin)) {
>+			ret = PTR_ERR(pins[i].pin);
>+			goto release_pins;
>+		}
>+	}
>+
>+	return 0;
>+
>+release_pins:
>+	while (--i >= 0)
>+		dpll_pin_put(pins[i].pin);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>+ * @dpll: dpll device pointer
>+ * @pins: pointer to pins array
>+ * @ops: callback ops registered with the pins
>+ * @count: number of pins
>+ *
>+ * Unregister pins of a given array of pins from given dpll device registered in
>+ * dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


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

No, it is not.


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

No, it is not.


>+ * If cgu is owned unregister pins from given dplls.
>+ * Release pins resources to the dpll subsystem.
>+ */
>+static void
>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
>+			    const struct dpll_pin_ops *ops,
>+			    struct dpll_device *first,
>+			    struct dpll_device *second)
>+{
>+	if (cgu) {
>+		ice_dpll_unregister_pins(first, pins, ops, count);
>+		ice_dpll_unregister_pins(second, pins, ops, count);
>+	}
>+	ice_dpll_release_pins(pins, count);
>+}
>+
>+/**
>+ * ice_dpll_init_direct_pins - initialize direct pins
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @pins: pointer to pins array
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @first: dpll device pointer
>+ * @second: dpll device pointer
>+ *
>+ * Allocate directly connected pins of a given array in dpll subsystem.
>+ * If cgu is owned register allocated pins with given dplls.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>+			  struct ice_dpll_pin *pins, int start_idx, int count,
>+			  const struct dpll_pin_ops *ops,
>+			  struct dpll_device *first, struct dpll_device *second)
>+{
>+	int ret;
>+
>+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
>+	if (ret)
>+		return ret;
>+	if (cgu) {
>+		ret = ice_dpll_register_pins(first, pins, ops, count);
>+		if (ret)
>+			goto release_pins;
>+		ret = ice_dpll_register_pins(second, pins, ops, count);
>+		if (ret)
>+			goto unregister_first;
>+	}
>+
>+	return 0;
>+
>+unregister_first:
>+	ice_dpll_unregister_pins(first, pins, ops, count);
>+release_pins:
>+	ice_dpll_release_pins(pins, count);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>+ * @pf: board private structure
>+ *
>+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+	struct dpll_pin *parent;
>+	int i;
>+
>+	for (i = 0; i < rclk->num_parents; i++) {
>+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>+		if (!parent)
>+			continue;
>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>+					   &ice_dpll_rclk_ops, rclk);
>+	}
>+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>+		return;
>+	netdev_dpll_pin_clear(vsi->netdev);
>+	dpll_pin_put(rclk->pin);
>+}
>+
>+/**
>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>+ * @pf: board private structure
>+ * @pin: pin to register
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @ops: callback ops registered with the pins
>+ *
>+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
>+ * pin with the parents it has in the info. Register pin with the pf's main vsi
>+ * netdev.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			int start_idx, const struct dpll_pin_ops *ops)
>+{
>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+	struct dpll_pin *parent;
>+	int ret, i;
>+
>+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>+				pf->dplls.clock_id);
>+	if (ret)
>+		return ret;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>+		if (!parent) {
>+			ret = -ENODEV;
>+			goto unregister_pins;
>+		}
>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>+					       ops, &pf->dplls.rclk);
>+		if (ret)
>+			goto unregister_pins;
>+	}
>+	if (WARN_ON((!vsi || !vsi->netdev)))
>+		return -EINVAL;
>+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>+
>+	return 0;
>+
>+unregister_pins:
>+	while (i) {
>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
>+	}
>+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_pins - deinitialize direct pins
>+ * @pf: board private structure
>+ * @cgu: if cgu is controlled by this pf
>+ *
>+ * If cgu is owned unregister directly connected pins from the dplls.
>+ * Release resources of directly connected pins from the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
>+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
>+	int num_outputs = pf->dplls.num_outputs;
>+	int num_inputs = pf->dplls.num_inputs;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+
>+	ice_dpll_deinit_rclk_pin(pf);
>+	if (cgu) {
>+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>+					 num_inputs);
>+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>+					 num_inputs);
>+	}
>+	ice_dpll_release_pins(inputs, num_inputs);
>+	if (cgu) {
>+		ice_dpll_unregister_pins(dp->dpll, outputs,
>+					 &ice_dpll_output_ops, num_outputs);
>+		ice_dpll_unregister_pins(de->dpll, outputs,
>+					 &ice_dpll_output_ops, num_outputs);
>+		ice_dpll_release_pins(outputs, num_outputs);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
>+ * subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>+{
>+	u32 rclk_idx;
>+	int ret;
>+
>+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>+					pf->dplls.num_inputs,
>+					&ice_dpll_input_ops,
>+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>+	if (ret)
>+		return ret;
>+	if (cgu) {
>+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>+						pf->dplls.num_inputs,
>+						pf->dplls.num_outputs,
>+						&ice_dpll_output_ops,
>+						pf->dplls.eec.dpll,
>+						pf->dplls.pps.dpll);
>+		if (ret)
>+			goto deinit_inputs;
>+	}
>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
>+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>+				      &ice_dpll_rclk_ops);
>+	if (ret)
>+		goto deinit_outputs;
>+
>+	return 0;
>+deinit_outputs:
>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>+				    pf->dplls.num_outputs,
>+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
>+				    pf->dplls.eec.dpll);
>+deinit_inputs:
>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
>+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
>+				    pf->dplls.eec.dpll);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>+ * @pf: board private structure
>+ * @d: pointer to ice_dpll
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * If cgu is owned unregister the dpll from dpll subsystem.
>+ * Release resources of dpll device from dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void
>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>+{
>+	if (cgu)
>+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>+	dpll_device_put(d->dpll);
>+}
>+
>+/**
>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>+ * @pf: board private structure
>+ * @d: dpll to be initialized
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @type: type of dpll being initialized
>+ *
>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
>+ * by this NIC, register dpll with the callback ops.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int
>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>+		   enum dpll_type type)
>+{
>+	u64 clock_id = pf->dplls.clock_id;
>+	int ret;
>+
>+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>+	if (IS_ERR(d->dpll)) {
>+		ret = PTR_ERR(d->dpll);
>+		dev_err(ice_pf_to_dev(pf),
>+			"dpll_device_get failed (%p) err=%d\n", d, ret);
>+		return ret;
>+	}
>+	d->pf = pf;
>+	if (cgu) {
>+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>+		if (ret) {
>+			dpll_device_put(d->dpll);
>+			return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>+ * @pf: board private structure
>+ *
>+ * Stop dpll's kworker, release it's resources.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+
>+	kthread_cancel_delayed_work_sync(&d->work);
>+	kthread_destroy_worker(d->kworker);
>+}
>+
>+/**
>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>+ * @pf: board private structure
>+ *
>+ * Create and start DPLLs periodic worker.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - create worker failure
>+ */
>+static int ice_dpll_init_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct kthread_worker *kworker;
>+
>+	ice_dpll_update_state(pf, &d->eec, true);
>+	ice_dpll_update_state(pf, &d->pps, true);
>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>+					dev_name(ice_pf_to_dev(pf)));
>+	if (IS_ERR(kworker))
>+		return PTR_ERR(kworker);
>+	d->kworker = kworker;
>+	d->cgu_state_acq_err_num = 0;
>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information for directly connected pins, cache them in pf's pins
>+ * structures.
>+ *
>+ * Context: Called under pf->dplls.lock.

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>+			       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>+	struct ice_hw *hw = &pf->hw;
>+	struct ice_dpll_pin *pins;
>+	int num_pins, i, ret;
>+	u8 freq_supp_num;
>+	bool input;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+		input = true;
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+		input = false;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>+		if (input) {
>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>+						      &de->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>+						      &dp->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			pins[i].prop.capabilities |=
>+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>+		}
>+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>+		if (ret)
>+			return ret;
>+		pins[i].prop.freq_supported =
>+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>+		pins[i].prop.freq_supported_num = freq_supp_num;
>+		pins[i].pf = pf;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
>+ * @pf: board private structure
>+ *
>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
>+
>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+	pin->pf = pf;
>+
>+	return ice_dpll_pin_state_update(pf, pin,
>+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
>+}
>+
>+/**
>+ * ice_dpll_init_pins_info - init pins info wrapper
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Wraps functions for pin initialization.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
>+{
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		return ice_dpll_init_info_direct_pins(pf, pin_type);
>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+		return ice_dpll_init_info_rclk_pin(pf);
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+/**
>+ * ice_dpll_deinit_info - release memory allocated for pins info
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>+{
>+	kfree(pf->dplls.inputs);
>+	kfree(pf->dplls.outputs);
>+	kfree(pf->dplls.eec.input_prio);
>+	kfree(pf->dplls.pps.input_prio);
>+}
>+
>+/**
>+ * 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).
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_aqc_get_cgu_abilities abilities;
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_hw *hw = &pf->hw;
>+	int ret, alloc_size, i;
>+
>+	d->clock_id = ice_generate_clock_id(pf);
>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"err:%d %s failed to read cgu abilities\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>+		return ret;
>+	}
>+
>+	de->dpll_idx = abilities.eec_dpll_idx;
>+	dp->dpll_idx = abilities.pps_dpll_idx;
>+	d->num_inputs = abilities.num_inputs;
>+	d->num_outputs = abilities.num_outputs;
>+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>+
>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->inputs)
>+		return -ENOMEM;
>+
>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!de->input_prio)
>+		return -ENOMEM;
>+
>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!dp->input_prio)
>+		return -ENOMEM;
>+
>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>+	if (ret)
>+		goto deinit_info;
>+
>+	if (cgu) {
>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+		if (!d->outputs)
>+			goto deinit_info;
>+
>+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+		if (ret)
>+			goto deinit_info;
>+	}
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>+					&pf->dplls.rclk.num_parents);
>+	if (ret)
>+		return ret;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>+	if (ret)
>+		return ret;
>+	de->mode = DPLL_MODE_AUTOMATIC;
>+	dp->mode = DPLL_MODE_AUTOMATIC;
>+
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>+
>+	return 0;
>+
>+deinit_info:
>+	dev_err(ice_pf_to_dev(pf),
>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>+		__func__, d->inputs, de->input_prio,
>+		dp->input_prio, d->outputs);
>+	ice_dpll_deinit_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * Handles the cleanup work required after dpll initialization,freeing resources
>+ * and unregistering the dpll, pin and all resources used for handling them.
>+ *
>+ * Context: Function holds pf->dplls.lock mutex.

No it does not. Update your comments. Or better, remove them,
they are totally useless anyway :/


>+ */
>+void ice_dpll_deinit(struct ice_pf *pf)
>+{
>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>+
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>+		return;
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+
>+	ice_dpll_deinit_pins(pf, cgu);
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>+	ice_dpll_deinit_info(pf);
>+	if (cgu)
>+		ice_dpll_deinit_worker(pf);

Could you please order the ice_dpll_deinit() to be symmetrical to
ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
ice_dpll_periodic_work() function is the only reason why you need it
currently.


>+	mutex_destroy(&pf->dplls.lock);
>+}
>+
>+/**
>+ * ice_dpll_init - initialize support for dpll subsystem
>+ * @pf: board private structure
>+ *
>+ * Set up the device dplls, register them and pins connected within Linux dpll
>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
>+ * configuration requests.
>+ *
>+ * Context: Function initializes and holds pf->dplls.lock mutex.

No, it does not hold it.


>+ */
>+void ice_dpll_init(struct ice_pf *pf)
>+{
>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>+	struct ice_dplls *d = &pf->dplls;
>+	int err = 0;
>+
>+	err = ice_dpll_init_info(pf, cgu);
>+	if (err)
>+		goto err_exit;
>+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
>+	if (err)
>+		goto deinit_info;
>+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
>+	if (err)
>+		goto deinit_eec;
>+	err = ice_dpll_init_pins(pf, cgu);
>+	if (err)
>+		goto deinit_pps;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	if (cgu) {
>+		err = ice_dpll_init_worker(pf);
>+		if (err)
>+			goto deinit_pins;
>+	}
>+
>+	return;
>+
>+deinit_pins:
>+	ice_dpll_deinit_pins(pf, cgu);
>+deinit_pps:
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>+deinit_eec:
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>+deinit_info:
>+	ice_dpll_deinit_info(pf);
>+err_exit:
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	mutex_unlock(&d->lock);

Leftover, please remove.


>+	mutex_destroy(&d->lock);
>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,104 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#ifndef _ICE_DPLL_H_
>+#define _ICE_DPLL_H_
>+
>+#include "ice.h"
>+
>+#define ICE_DPLL_PRIO_MAX	0xF
>+#define ICE_DPLL_RCLK_NUM_MAX	4
>+
>+/** ice_dpll_pin - store info about pins
>+ * @pin: dpll pin structure
>+ * @pf: pointer to pf, which has registered the dpll_pin
>+ * @idx: ice pin private idx
>+ * @num_parents: hols number of parent pins
>+ * @parent_idx: hold indexes of parent pins
>+ * @flags: pin flags returned from HW
>+ * @state: state of a pin
>+ * @prop: pin properities
>+ * @freq: current frequency of a pin
>+ */
>+struct ice_dpll_pin {
>+	struct dpll_pin *pin;
>+	struct ice_pf *pf;
>+	u8 idx;
>+	u8 num_parents;
>+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>+	struct dpll_pin_properties prop;
>+	u32 freq;
>+};
>+
>+/** ice_dpll - store info required for DPLL control
>+ * @dpll: pointer to dpll dev
>+ * @pf: pointer to pf, which has registered the dpll_device
>+ * @dpll_idx: index of dpll on the NIC
>+ * @input_idx: currently selected input index
>+ * @prev_input_idx: previously selected input index
>+ * @ref_state: state of dpll reference signals
>+ * @eec_mode: eec_mode dpll is configured for
>+ * @phase_shift: phase shift delay of a dpll
>+ * @input_prio: priorities of each input
>+ * @dpll_state: current dpll sync state
>+ * @prev_dpll_state: last dpll sync state
>+ * @active_input: pointer to active input pin
>+ * @prev_input: pointer to previous active input pin
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	struct ice_pf *pf;
>+	u8 dpll_idx;
>+	u8 input_idx;
>+	u8 prev_input_idx;
>+	u8 ref_state;
>+	u8 eec_mode;
>+	s64 phase_shift;
>+	u8 *input_prio;
>+	enum dpll_lock_status dpll_state;
>+	enum dpll_lock_status prev_dpll_state;
>+	enum dpll_mode mode;
>+	struct dpll_pin *active_input;
>+	struct dpll_pin *prev_input;
>+};
>+
>+/** ice_dplls - store info required for CCU (clock controlling unit)
>+ * @kworker: periodic worker
>+ * @work: periodic work
>+ * @lock: locks access to configuration of a dpll
>+ * @eec: pointer to EEC dpll dev
>+ * @pps: pointer to PPS dpll dev
>+ * @inputs: input pins pointer
>+ * @outputs: output pins pointer
>+ * @rclk: recovered pins pointer
>+ * @num_inputs: number of input pins available on dpll
>+ * @num_outputs: number of output pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>+ * @clock_id: clock_id of dplls
>+ */
>+struct ice_dplls {
>+	struct kthread_worker *kworker;
>+	struct kthread_delayed_work work;
>+	struct mutex lock;
>+	struct ice_dpll eec;
>+	struct ice_dpll pps;
>+	struct ice_dpll_pin *inputs;
>+	struct ice_dpll_pin *outputs;
>+	struct ice_dpll_pin rclk;
>+	u8 num_inputs;
>+	u8 num_outputs;
>+	int cgu_state_acq_err_num;
>+	u8 base_rclk_idx;
>+	u64 clock_id;
>+	s32 input_phase_adj_max;
>+	s32 output_phase_adj_max;
>+};
>+
>+void ice_dpll_init(struct ice_pf *pf);
>+
>+void ice_dpll_deinit(struct ice_pf *pf);
>+
>+#endif
>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
>index 19a5e7f3a075..0a94daaf3d20 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4613,6 +4613,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");
>@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
> 		ice_gnss_exit(pf);
> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
> 		ice_ptp_release(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>+	    ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_deinit(pf);
> }
> 
> static void ice_init_wakeup(struct ice_pf *pf)
>-- 
>2.27.0
>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 11:39     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 11:39 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Control over clock generation unit is required for further development
>of Synchronous Ethernet feature. Interface provides ability to obtain
>current state of a dpll, its sources and outputs which are pins, and
>allows their configuration.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
>RFC v9->v0:
>- add support for DPLL_MODE_FREERUN mode
>- use DPLL_LOCK_STATUS, remove ICE_DPLL_LOCK_STATUS
>- fix mutex locking scheme
>- remove rclk pin label
>- fix/remmove struct fields descriptions
>
>v8->v9:
>- drop pointless 0 assignement
>- ice_dpll_init(..) returns void instead of int
>- fix context description of the functions
>- fix ice_dpll_init(..) traces
>- fix use package_label instead pf board_label for rclk pin
>- be consistent on cgu presence naming
>- remove indent in ice_dpll_deinit(..)
>- remove unused struct field lock_err_num
>- fix kworker resched behavior
>- remove debug log from ice_dpll_deinit_worker(..)
>- reorder ice internal functions
>- release resources directly on error path
>- remove redundant NULL checks when releasing resources
>- do not assign NULL to pointers after releasing resources
>- simplify variable assignement
>- fix 'int ret;' declarations across the ice_dpll.c
>- remove leftover ice_dpll_find(..)
>- get pf pointer from dpll_priv without type cast
>- improve error reporting
>- fix documentation
>- fix ice_dpll_update_state(..) flow
>- fix return in case out of range prio set
>
> drivers/net/ethernet/intel/Kconfig        |    1 +
> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
> drivers/net/ethernet/intel/ice/ice.h      |    3 +
> drivers/net/ethernet/intel/ice/ice_dpll.c | 2053 +++++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h |  104 ++
> drivers/net/ethernet/intel/ice/ice_main.c |    7 +
> 6 files changed, 2170 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>
>diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
>index 9bc0a9519899..913dcf928d15 100644
>--- a/drivers/net/ethernet/intel/Kconfig
>+++ b/drivers/net/ethernet/intel/Kconfig
>@@ -284,6 +284,7 @@ config ICE
> 	select DIMLIB
> 	select NET_DEVLINK
> 	select PLDMFW
>+	select DPLL
> 	help
> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
> 	  devices.  For more information on how to identify your adapter, go
>diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
>index 817977e3039d..85d6366d1f5b 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -34,7 +34,8 @@ ice-y := ice_main.o	\
> 	 ice_lag.o	\
> 	 ice_ethtool.o  \
> 	 ice_repr.o	\
>-	 ice_tc_lib.o
>+	 ice_tc_lib.o	\
>+	 ice_dpll.o
> ice-$(CONFIG_PCI_IOV) +=	\
> 	ice_sriov.o		\
> 	ice_virtchnl.o		\
>diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
>index 484d1d143174..a520141ef665 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -76,6 +76,7 @@
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
> #include "ice_irq.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -507,6 +508,7 @@ enum ice_pf_flags {
> 	ICE_FLAG_UNPLUG_AUX_DEV,
> 	ICE_FLAG_MTU_CHANGED,
> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
> 	ICE_PF_FLAGS_NBITS		/* must be last */
> };
> 
>@@ -636,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..ba319cfb9167
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,2053 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#include "ice.h"
>+#include "ice_lib.h"
>+#include "ice_trace.h"
>+#include <linux/dpll.h>
>+
>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
>+#define ICE_DPLL_LOCK_TRIES			1000
>+#define ICE_DPLL_PIN_IDX_INVALID		0xff
>+#define ICE_DPLL_RCLK_NUM_PER_PF		1
>+
>+/**
>+ * enum ice_dpll_pin_type - enumerate ice pin types:
>+ * @ICE_DPLL_PIN_INVALID: invalid pin type
>+ * @ICE_DPLL_PIN_TYPE_INPUT: input pin
>+ * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
>+ * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
>+ */
>+enum ice_dpll_pin_type {
>+	ICE_DPLL_PIN_INVALID,
>+	ICE_DPLL_PIN_TYPE_INPUT,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>+};
>+
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_INPUT] = "input",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
>+};
>+
>+/**
>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>+ * @pf: private board structure
>+ * @extack: error reporting
>+ *
>+ * 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.

I don't know, I will probably need to beg you here. Why exactly are you
ignoring my comments? It's not nice, I thought we are way past it...

There is no "dead lock". Could you please describe how exactly
the case you mention can happen? It can't.
Could you please remove the trylock iteration below?
It's completely pointless.



>+ *
>+ * Return:
>+ * 0 - if lock acquired
>+ * negative - lock not acquired or dpll is not initialized
>+ */
>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack *extack)
>+{
>+	int i;
>+
>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {

And again, as I already told you, this flag checking is totally
pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().



>+			if (extack)
>+				NL_SET_ERR_MSG(extack,
>+					       "ice dpll not initialized");
>+			return -EFAULT;
>+		}
>+		if (mutex_trylock(&pf->dplls.lock))
>+			return 0;
>+		usleep_range(100, 150);
>+	}
>+	if (extack)

No need to check for NULL, but this code should be dropped anyway.

>+		NL_SET_ERR_MSG(extack, "was not able to acquire mutex");
>+
>+	return -EBUSY;
>+}
>+
>+/**
>+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>+ * @pf: private board structure
>+ *
>+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
>+ */
>+static void ice_dpll_cb_unlock(struct ice_pf *pf)
>+{
>+	mutex_unlock(&pf->dplls.lock);
>+}
>+
>+/**
>+ * ice_dpll_pin_freq_set - set pin's frequency
>+ * @pf: private board structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being configured
>+ * @freq: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Set requested frequency on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error on AQ or wrong pin type given
>+ */
>+static int
>+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+		      enum ice_dpll_pin_type pin_type, const u32 freq,
>+		      struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+	u8 flags;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>+					       pin->flags[0], freq, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret) {
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   freq, pin->idx);
>+		return ret;
>+	}
>+	pin->freq = freq;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       const u32 frequency,
>+		       struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_set - input pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+			     const struct dpll_device *dpll, void *dpll_priv,
>+			     u64 frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_set - output pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+			      const struct dpll_device *dpll, void *dpll_priv,
>+			      u64 frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       u64 *frequency, struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*frequency = p->freq;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_get - input pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a input pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+			     const struct dpll_device *dpll, void *dpll_priv,
>+			     u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_get - output pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+			      const struct dpll_device *dpll, void *dpll_priv,
>+			      u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_enable - enable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being enabled
>+ * @extack: error reporting
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    enum ice_dpll_pin_type pin_type,
>+		    struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to enable %s pin:%u\n",
>+				   ret, ice_aq_str(hw->adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_disable - disable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being disabled
>+ * @extack: error reporting
>+ *
>+ * Disable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		     enum ice_dpll_pin_type pin_type,
>+		     struct netlink_ext_ack *extack)
>+{
>+	u8 flags = 0;
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to disable %s pin:%u\n",
>+				   ret, ice_aq_str(hw->adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_update - update pin's state
>+ * @pf: private board struct
>+ * @pin: structure with pin attributes to be updated
>+ * @pin_type: type of pin being updated
>+ * @extack: error reporting
>+ *
>+ * Determine pin current state and frequency, then update struct
>+ * holding the pin info. For input pin states are separated for each
>+ * dpll, for rclk pins states are separated for each parent.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			  enum ice_dpll_pin_type pin_type,
>+			  struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>+					       NULL, &pin->flags[0],
>+					       &pin->freq, NULL);
>+		if (ret)
>+			goto err;
>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>+			if (pin->pin) {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					pin->pin == pf->dplls.eec.active_input ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					pin->pin == pf->dplls.pps.active_input ?
>+					DPLL_PIN_STATE_CONNECTED :
>+					DPLL_PIN_STATE_SELECTABLE;
>+			} else {
>+				pin->state[pf->dplls.eec.dpll_idx] =
>+					DPLL_PIN_STATE_SELECTABLE;
>+				pin->state[pf->dplls.pps.dpll_idx] =
>+					DPLL_PIN_STATE_SELECTABLE;
>+			}
>+		} else {
>+			pin->state[pf->dplls.eec.dpll_idx] =
>+				DPLL_PIN_STATE_DISCONNECTED;
>+			pin->state[pf->dplls.pps.dpll_idx] =
>+				DPLL_PIN_STATE_DISCONNECTED;
>+		}
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>+						&pin->flags[0], NULL,
>+						&pin->freq, NULL);
>+		if (ret)
>+			goto err;
>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>+			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>+		else
>+			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>+		break;
>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>+
>+		for (parent = 0; parent < pf->dplls.rclk.num_parents;
>+		     parent++) {
>+			u8 p = parent;
>+
>+			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
>+							 &port_num,
>+							 &pin->flags[parent],
>+							 NULL);
>+			if (ret)
>+				goto err;
>+			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>+			    pin->flags[parent])
>+				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>+			else
>+				pin->state[parent] =
>+					DPLL_PIN_STATE_DISCONNECTED;
>+		}
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+
>+	return 0;
>+err:
>+	if (extack)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to update %s pin:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   pin_type_name[pin_type], pin->idx);
>+	else
>+		dev_err_ratelimited(ice_pf_to_dev(pf),
>+				    "err:%d %s failed to update %s pin:%u\n",
>+				    ret,
>+				    ice_aq_str(pf->hw.adminq.sq_last_status),
>+				    pin_type_name[pin_type], pin->idx);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
>+ * @pf: board private structure
>+ * @dpll: ice dpll pointer
>+ * @pin: ice pin pointer
>+ * @prio: priority value being set on a dpll
>+ * @extack: error reporting
>+ *
>+ * Internal wrapper for setting the priority in the hardware.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>+			   struct ice_dpll_pin *pin, const u32 prio,
>+			   struct netlink_ext_ack *extack)
>+{
>+	int ret;
>+
>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>+				      (u8)prio);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "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
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @status: on success holds dpll's lock status
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback, provides dpll's lock status.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_lock_status *status,
>+			 struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*status = d->dpll_state;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_supported - check if dpll's working mode is supported
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: mode to be checked for support
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides information if working mode is supported
>+ * by dpll.
>+ *
>+ * Return:
>+ * * true - mode is supported
>+ * * false - mode is not supported
>+ */
>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>+				    void *dpll_priv,
>+				    enum dpll_mode mode,
>+				    struct netlink_ext_ack *extack)
>+{
>+	if (mode == DPLL_MODE_AUTOMATIC ||
>+	    mode == DPLL_MODE_FREERUN)
>+		return true;
>+
>+	return false;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - get dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: on success holds current working mode of dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
>+			     enum dpll_mode *mode,
>+			     struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*mode = d->mode;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_mode_set - set dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @mode: requested working mode of dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. User requests working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
>+			     enum dpll_mode mode,
>+			     struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	u8 config;
>+	int ret;
>+
>+	switch (mode) {
>+	case DPLL_MODE_AUTOMATIC:
>+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_AUTOMATIC;
>+		break;
>+	case DPLL_MODE_FREERUN:
>+		config = ICE_AQC_SET_CGU_DPLL_CONFIG_MODE_FREERUN;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_aq_set_cgu_dpll_config(&pf->hw, d->dpll_idx, d->ref_state,
>+					 config, d->eec_mode);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set mode:%u on dpll:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   mode, d->dpll_idx);
>+	else
>+		d->mode = mode;
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_set - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @enable: if pin shalll be enabled
>+ * @extack: error reporting
>+ * @pin_type: type of a pin
>+ *
>+ * Set pin state on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       bool enable, struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	if (enable)
>+		ret = ice_dpll_pin_enable(&pf->hw, p, pin_type, extack);
>+	else
>+		ret = ice_dpll_pin_disable(&pf->hw, p, pin_type, extack);
>+	if (!ret)
>+		ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Set given state on output type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_state state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
>+
>+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Enables given mode on input type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_state state,
>+			 struct netlink_ext_ack *extack)
>+{
>+	bool enable = state == DPLL_PIN_STATE_SELECTABLE;
>+
>+	return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_state_get - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ * @pin_type: type of questioned pin
>+ *
>+ * Determine pin state set it on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
>+		       const struct dpll_device *dpll, void *dpll_priv,
>+		       enum dpll_pin_state *state,
>+		       struct netlink_ext_ack *extack,
>+		       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
>+	if (ret)
>+		goto unlock;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
>+		*state = p->state[d->dpll_idx];
>+	else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>+		*state = p->state[0];
>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_get - get output pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_state *state,
>+			  struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_get - get input pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_state *state,
>+			 struct netlink_ext_ack *extack)
>+{
>+	return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_prio_get - get dpll's input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: on success - returns input priority on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
>+			const struct dpll_device *dpll, void *dpll_priv,
>+			u32 *prio, struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	*prio = d->input_prio[p->idx];
>+	ice_dpll_cb_unlock(pf);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_prio_set - set dpll input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: input priority to be set on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
>+			const struct dpll_device *dpll, void *dpll_priv,
>+			u32 prio, struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv;
>+	struct ice_dpll *d = dpll_priv;
>+	struct ice_pf *pf = d->pf;
>+	int ret;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX) {
>+		NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
>+				   ICE_DPLL_PRIO_MAX);
>+		return -EINVAL;
>+	}
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack);
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_direction - callback for get input pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds input pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of a input pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
>+			 const struct dpll_device *dpll, void *dpll_priv,
>+			 enum dpll_pin_direction *direction,
>+			 struct netlink_ext_ack *extack)
>+{
>+	*direction = DPLL_PIN_DIRECTION_INPUT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_output_direction - callback for get output pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds output pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of an output pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
>+			  const struct dpll_device *dpll, void *dpll_priv,
>+			  enum dpll_pin_direction *direction,
>+			  struct netlink_ext_ack *extack)
>+{
>+	*direction = DPLL_PIN_DIRECTION_OUTPUT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @parent_pin_priv: parent private data pointer passed on pin registration
>+ * @state: state to be set on pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
>+			       const struct dpll_pin *parent_pin,
>+			       void *parent_pin_priv,
>+			       enum dpll_pin_state state,
>+			       struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+	bool enable = state == DPLL_PIN_STATE_CONNECTED;
>+	struct ice_pf *pf = p->pf;
>+	u32 hw_idx;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+	if (hw_idx >= pf->dplls.num_inputs)
>+		goto unlock;
>+
>+	if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
>+	    (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "pin:%u state:%u on parent:%u already set",
>+				   p->idx, state, parent->idx);
>+		ret = -EINVAL;
>+		goto unlock;
>+	}
>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
>+					 &p->freq);
>+	if (ret)
>+		NL_SET_ERR_MSG_FMT(extack,
>+				   "err:%d %s failed to set pin state:%u for pin:%u on parent:%u\n",
>+				   ret,
>+				   ice_aq_str(pf->hw.adminq.sq_last_status),
>+				   state, p->idx, parent->idx);
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @parent_pin_priv: pin parent priv data pointer passed on pin registration
>+ * @state: on success holds pin state on parent pin
>+ * @extack: error reporting
>+ *
>+ * dpll subsystem callback, get a state of a recovered clock pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
>+			       const struct dpll_pin *parent_pin,
>+			       void *parent_pin_priv,
>+			       enum dpll_pin_state *state,
>+			       struct netlink_ext_ack *extack)
>+{
>+	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+	struct ice_pf *pf = p->pf;
>+	u32 hw_idx;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, extack);
>+	if (ret)
>+		return ret;
>+	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+	if (hw_idx >= pf->dplls.num_inputs)
>+		goto unlock;
>+
>+	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>+					extack);
>+	if (ret)
>+		goto unlock;
>+
>+	*state = p->state[hw_idx];
>+	ret = 0;
>+unlock:
>+	ice_dpll_cb_unlock(pf);
>+
>+	return ret;
>+}
>+
>+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
>+	.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
>+	.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
>+	.direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_input_ops = {
>+	.frequency_get = ice_dpll_input_frequency_get,
>+	.frequency_set = ice_dpll_input_frequency_set,
>+	.state_on_dpll_get = ice_dpll_input_state_get,
>+	.state_on_dpll_set = ice_dpll_input_state_set,
>+	.prio_get = ice_dpll_input_prio_get,
>+	.prio_set = ice_dpll_input_prio_set,
>+	.direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_output_ops = {
>+	.frequency_get = ice_dpll_output_frequency_get,
>+	.frequency_set = ice_dpll_output_frequency_set,
>+	.state_on_dpll_get = ice_dpll_output_state_get,
>+	.state_on_dpll_set = ice_dpll_output_state_set,
>+	.direction_get = ice_dpll_output_direction,
>+};
>+
>+static const struct dpll_device_ops ice_dpll_ops = {
>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_set = ice_dpll_mode_set,
>+};
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ *
>+ * Return: generated clock id for the board
>+ */
>+static u64 ice_generate_clock_id(struct ice_pf *pf)
>+{
>+	return pci_get_dsn(pf->pdev);
>+}
>+
>+/**
>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>+ * @d: pointer do dpll
>+ *
>+ * Once change detected appropriate event is submitted to the dpll subsystem.
>+ */
>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>+{
>+	if (d->prev_dpll_state != d->dpll_state) {
>+		d->prev_dpll_state = d->dpll_state;
>+		dpll_device_change_ntf(d->dpll);
>+	}
>+	if (d->prev_input != d->active_input) {
>+		if (d->prev_input)
>+			dpll_pin_change_ntf(d->prev_input);
>+		d->prev_input = d->active_input;
>+		if (d->active_input)
>+			dpll_pin_change_ntf(d->active_input);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_update_state - update dpll state
>+ * @pf: pf private structure
>+ * @d: pointer to queried dpll device
>+ * @init: if function called on initialization of ice dpll
>+ *
>+ * Poll current state of dpll from hw and update ice_dpll struct.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - AQ failure
>+ */
>+static int
>+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>+{
>+	struct ice_dpll_pin *p = NULL;
>+	int ret;
>+
>+	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>+				&d->input_idx, &d->ref_state, &d->eec_mode,
>+				&d->phase_shift, &d->dpll_state, &d->mode);
>+
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
>+		d->dpll_idx, d->prev_input_idx, d->input_idx,
>+		d->dpll_state, d->prev_dpll_state, d->mode);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"update dpll=%d state failed, ret=%d %s\n",
>+			d->dpll_idx, ret,
>+			ice_aq_str(pf->hw.adminq.sq_last_status));
>+		return ret;
>+	}
>+	if (init) {
>+		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
>+		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
>+			d->active_input = pf->dplls.inputs[d->input_idx].pin;
>+		p = &pf->dplls.inputs[d->input_idx];
>+		return ice_dpll_pin_state_update(pf, p,
>+						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
>+	}
>+	if (d->dpll_state == DPLL_LOCK_STATUS_HOLDOVER ||
>+	    d->dpll_state == DPLL_LOCK_STATUS_UNLOCKED) {
>+		d->active_input = NULL;
>+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID)
>+			p = &pf->dplls.inputs[d->input_idx];
>+		d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+		if (!p)
>+			return 0;
>+		ret = ice_dpll_pin_state_update(pf, p,
>+						ICE_DPLL_PIN_TYPE_INPUT, NULL);
>+	} else if (d->input_idx != d->prev_input_idx) {
>+		if (d->prev_input_idx != ICE_DPLL_PIN_IDX_INVALID) {
>+			p = &pf->dplls.inputs[d->prev_input_idx];
>+			ice_dpll_pin_state_update(pf, p,
>+						  ICE_DPLL_PIN_TYPE_INPUT,
>+						  NULL);
>+		}
>+		if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID) {
>+			p = &pf->dplls.inputs[d->input_idx];
>+			d->active_input = p->pin;
>+			ice_dpll_pin_state_update(pf, p,
>+						  ICE_DPLL_PIN_TYPE_INPUT,
>+						  NULL);
>+		}
>+		d->prev_input_idx = d->input_idx;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ * Context: Holds pf->dplls.lock
>+ */
>+static void ice_dpll_periodic_work(struct kthread_work *work)
>+{
>+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	int ret;
>+
>+	ret = ice_dpll_cb_lock(pf, NULL);
>+	if (ret == -EBUSY)
>+		goto resched;
>+	else if (ret)
>+		return;
>+	ret = ice_dpll_update_state(pf, de, false);
>+	if (!ret)
>+		ret = ice_dpll_update_state(pf, dp, false);
>+	if (ret) {
>+		d->cgu_state_acq_err_num++;
>+		/* stop rescheduling this worker */
>+		if (d->cgu_state_acq_err_num >
>+		    ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
>+			dev_err(ice_pf_to_dev(pf),
>+				"EEC/PPS DPLLs periodic work disabled\n");
>+			return;
>+		}
>+	}
>+	ice_dpll_cb_unlock(pf);
>+	ice_dpll_notify_changes(de);
>+	ice_dpll_notify_changes(dp);
>+
>+resched:
>+	/* Run twice a second or reschedule if update failed */
>+	kthread_queue_delayed_work(d->kworker, &d->work,
>+				   ret ? msecs_to_jiffies(10) :
>+				   msecs_to_jiffies(500));
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ *
>+ * Release resources of given pins array in the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>+{
>+	int i;
>+
>+	for (i = 0; i < count; i++)
>+		dpll_pin_put(pins[i].pin);
>+}
>+
>+/**
>+ * ice_dpll_get_pins - get pins from dpll subsystem
>+ * @pf: board private structure
>+ * @pins: pointer to pins array
>+ * @start_idx: get starts from this pin idx value
>+ * @count: number of pins
>+ * @clock_id: clock_id of dpll device
>+ *
>+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
>+ * pins array.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation failure reason
>+ */
>+static int
>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>+		  int start_idx, int count, u64 clock_id)
>+{
>+	int i, ret;
>+
>+	for (i = 0; i < count; i++) {
>+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
>+					   &pins[i].prop);
>+		if (IS_ERR(pins[i].pin)) {
>+			ret = PTR_ERR(pins[i].pin);
>+			goto release_pins;
>+		}
>+	}
>+
>+	return 0;
>+
>+release_pins:
>+	while (--i >= 0)
>+		dpll_pin_put(pins[i].pin);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>+ * @dpll: dpll device pointer
>+ * @pins: pointer to pins array
>+ * @ops: callback ops registered with the pins
>+ * @count: number of pins
>+ *
>+ * Unregister pins of a given array of pins from given dpll device registered in
>+ * dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


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

No, it is not.


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

No, it is not.


>+ * If cgu is owned unregister pins from given dplls.
>+ * Release pins resources to the dpll subsystem.
>+ */
>+static void
>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
>+			    const struct dpll_pin_ops *ops,
>+			    struct dpll_device *first,
>+			    struct dpll_device *second)
>+{
>+	if (cgu) {
>+		ice_dpll_unregister_pins(first, pins, ops, count);
>+		ice_dpll_unregister_pins(second, pins, ops, count);
>+	}
>+	ice_dpll_release_pins(pins, count);
>+}
>+
>+/**
>+ * ice_dpll_init_direct_pins - initialize direct pins
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @pins: pointer to pins array
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @first: dpll device pointer
>+ * @second: dpll device pointer
>+ *
>+ * Allocate directly connected pins of a given array in dpll subsystem.
>+ * If cgu is owned register allocated pins with given dplls.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>+			  struct ice_dpll_pin *pins, int start_idx, int count,
>+			  const struct dpll_pin_ops *ops,
>+			  struct dpll_device *first, struct dpll_device *second)
>+{
>+	int ret;
>+
>+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
>+	if (ret)
>+		return ret;
>+	if (cgu) {
>+		ret = ice_dpll_register_pins(first, pins, ops, count);
>+		if (ret)
>+			goto release_pins;
>+		ret = ice_dpll_register_pins(second, pins, ops, count);
>+		if (ret)
>+			goto unregister_first;
>+	}
>+
>+	return 0;
>+
>+unregister_first:
>+	ice_dpll_unregister_pins(first, pins, ops, count);
>+release_pins:
>+	ice_dpll_release_pins(pins, count);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>+ * @pf: board private structure
>+ *
>+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+	struct dpll_pin *parent;
>+	int i;
>+
>+	for (i = 0; i < rclk->num_parents; i++) {
>+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>+		if (!parent)
>+			continue;
>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>+					   &ice_dpll_rclk_ops, rclk);
>+	}
>+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>+		return;
>+	netdev_dpll_pin_clear(vsi->netdev);
>+	dpll_pin_put(rclk->pin);
>+}
>+
>+/**
>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>+ * @pf: board private structure
>+ * @pin: pin to register
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @ops: callback ops registered with the pins
>+ *
>+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
>+ * pin with the parents it has in the info. Register pin with the pf's main vsi
>+ * netdev.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			int start_idx, const struct dpll_pin_ops *ops)
>+{
>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+	struct dpll_pin *parent;
>+	int ret, i;
>+
>+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>+				pf->dplls.clock_id);
>+	if (ret)
>+		return ret;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>+		if (!parent) {
>+			ret = -ENODEV;
>+			goto unregister_pins;
>+		}
>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>+					       ops, &pf->dplls.rclk);
>+		if (ret)
>+			goto unregister_pins;
>+	}
>+	if (WARN_ON((!vsi || !vsi->netdev)))
>+		return -EINVAL;
>+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>+
>+	return 0;
>+
>+unregister_pins:
>+	while (i) {
>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
>+	}
>+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_pins - deinitialize direct pins
>+ * @pf: board private structure
>+ * @cgu: if cgu is controlled by this pf
>+ *
>+ * If cgu is owned unregister directly connected pins from the dplls.
>+ * Release resources of directly connected pins from the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
>+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
>+	int num_outputs = pf->dplls.num_outputs;
>+	int num_inputs = pf->dplls.num_inputs;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+
>+	ice_dpll_deinit_rclk_pin(pf);
>+	if (cgu) {
>+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>+					 num_inputs);
>+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>+					 num_inputs);
>+	}
>+	ice_dpll_release_pins(inputs, num_inputs);
>+	if (cgu) {
>+		ice_dpll_unregister_pins(dp->dpll, outputs,
>+					 &ice_dpll_output_ops, num_outputs);
>+		ice_dpll_unregister_pins(de->dpll, outputs,
>+					 &ice_dpll_output_ops, num_outputs);
>+		ice_dpll_release_pins(outputs, num_outputs);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
>+ * subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>+{
>+	u32 rclk_idx;
>+	int ret;
>+
>+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>+					pf->dplls.num_inputs,
>+					&ice_dpll_input_ops,
>+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>+	if (ret)
>+		return ret;
>+	if (cgu) {
>+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>+						pf->dplls.num_inputs,
>+						pf->dplls.num_outputs,
>+						&ice_dpll_output_ops,
>+						pf->dplls.eec.dpll,
>+						pf->dplls.pps.dpll);
>+		if (ret)
>+			goto deinit_inputs;
>+	}
>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
>+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>+				      &ice_dpll_rclk_ops);
>+	if (ret)
>+		goto deinit_outputs;
>+
>+	return 0;
>+deinit_outputs:
>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>+				    pf->dplls.num_outputs,
>+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
>+				    pf->dplls.eec.dpll);
>+deinit_inputs:
>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
>+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
>+				    pf->dplls.eec.dpll);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>+ * @pf: board private structure
>+ * @d: pointer to ice_dpll
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * If cgu is owned unregister the dpll from dpll subsystem.
>+ * Release resources of dpll device from dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void
>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>+{
>+	if (cgu)
>+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>+	dpll_device_put(d->dpll);
>+}
>+
>+/**
>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>+ * @pf: board private structure
>+ * @d: dpll to be initialized
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @type: type of dpll being initialized
>+ *
>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
>+ * by this NIC, register dpll with the callback ops.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int
>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>+		   enum dpll_type type)
>+{
>+	u64 clock_id = pf->dplls.clock_id;
>+	int ret;
>+
>+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>+	if (IS_ERR(d->dpll)) {
>+		ret = PTR_ERR(d->dpll);
>+		dev_err(ice_pf_to_dev(pf),
>+			"dpll_device_get failed (%p) err=%d\n", d, ret);
>+		return ret;
>+	}
>+	d->pf = pf;
>+	if (cgu) {
>+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>+		if (ret) {
>+			dpll_device_put(d->dpll);
>+			return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>+ * @pf: board private structure
>+ *
>+ * Stop dpll's kworker, release it's resources.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+
>+	kthread_cancel_delayed_work_sync(&d->work);
>+	kthread_destroy_worker(d->kworker);
>+}
>+
>+/**
>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>+ * @pf: board private structure
>+ *
>+ * Create and start DPLLs periodic worker.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - create worker failure
>+ */
>+static int ice_dpll_init_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct kthread_worker *kworker;
>+
>+	ice_dpll_update_state(pf, &d->eec, true);
>+	ice_dpll_update_state(pf, &d->pps, true);
>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>+					dev_name(ice_pf_to_dev(pf)));
>+	if (IS_ERR(kworker))
>+		return PTR_ERR(kworker);
>+	d->kworker = kworker;
>+	d->cgu_state_acq_err_num = 0;
>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information for directly connected pins, cache them in pf's pins
>+ * structures.
>+ *
>+ * Context: Called under pf->dplls.lock.

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>+			       enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>+	struct ice_hw *hw = &pf->hw;
>+	struct ice_dpll_pin *pins;
>+	int num_pins, i, ret;
>+	u8 freq_supp_num;
>+	bool input;
>+
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+		input = true;
>+		break;
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+		input = false;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>+		if (input) {
>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>+						      &de->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>+						      &dp->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			pins[i].prop.capabilities |=
>+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>+		}
>+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>+		if (ret)
>+			return ret;
>+		pins[i].prop.freq_supported =
>+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>+		pins[i].prop.freq_supported_num = freq_supp_num;
>+		pins[i].pf = pf;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
>+ * @pf: board private structure
>+ *
>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
>+
>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+	pin->pf = pf;
>+
>+	return ice_dpll_pin_state_update(pf, pin,
>+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
>+}
>+
>+/**
>+ * ice_dpll_init_pins_info - init pins info wrapper
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Wraps functions for pin initialization.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
>+{
>+	switch (pin_type) {
>+	case ICE_DPLL_PIN_TYPE_INPUT:
>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>+		return ice_dpll_init_info_direct_pins(pf, pin_type);
>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+		return ice_dpll_init_info_rclk_pin(pf);
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+/**
>+ * ice_dpll_deinit_info - release memory allocated for pins info
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ */
>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>+{
>+	kfree(pf->dplls.inputs);
>+	kfree(pf->dplls.outputs);
>+	kfree(pf->dplls.eec.input_prio);
>+	kfree(pf->dplls.pps.input_prio);
>+}
>+
>+/**
>+ * 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).
>+ *
>+ * Context: Called under pf->dplls.lock

No, it is not.


>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>+{
>+	struct ice_aqc_get_cgu_abilities abilities;
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_hw *hw = &pf->hw;
>+	int ret, alloc_size, i;
>+
>+	d->clock_id = ice_generate_clock_id(pf);
>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"err:%d %s failed to read cgu abilities\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>+		return ret;
>+	}
>+
>+	de->dpll_idx = abilities.eec_dpll_idx;
>+	dp->dpll_idx = abilities.pps_dpll_idx;
>+	d->num_inputs = abilities.num_inputs;
>+	d->num_outputs = abilities.num_outputs;
>+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>+
>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->inputs)
>+		return -ENOMEM;
>+
>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!de->input_prio)
>+		return -ENOMEM;
>+
>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!dp->input_prio)
>+		return -ENOMEM;
>+
>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>+	if (ret)
>+		goto deinit_info;
>+
>+	if (cgu) {
>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+		if (!d->outputs)
>+			goto deinit_info;
>+
>+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+		if (ret)
>+			goto deinit_info;
>+	}
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>+					&pf->dplls.rclk.num_parents);
>+	if (ret)
>+		return ret;
>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>+	if (ret)
>+		return ret;
>+	de->mode = DPLL_MODE_AUTOMATIC;
>+	dp->mode = DPLL_MODE_AUTOMATIC;
>+
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>+
>+	return 0;
>+
>+deinit_info:
>+	dev_err(ice_pf_to_dev(pf),
>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>+		__func__, d->inputs, de->input_prio,
>+		dp->input_prio, d->outputs);
>+	ice_dpll_deinit_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * Handles the cleanup work required after dpll initialization,freeing resources
>+ * and unregistering the dpll, pin and all resources used for handling them.
>+ *
>+ * Context: Function holds pf->dplls.lock mutex.

No it does not. Update your comments. Or better, remove them,
they are totally useless anyway :/


>+ */
>+void ice_dpll_deinit(struct ice_pf *pf)
>+{
>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>+
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>+		return;
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+
>+	ice_dpll_deinit_pins(pf, cgu);
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>+	ice_dpll_deinit_info(pf);
>+	if (cgu)
>+		ice_dpll_deinit_worker(pf);

Could you please order the ice_dpll_deinit() to be symmetrical to
ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
ice_dpll_periodic_work() function is the only reason why you need it
currently.


>+	mutex_destroy(&pf->dplls.lock);
>+}
>+
>+/**
>+ * ice_dpll_init - initialize support for dpll subsystem
>+ * @pf: board private structure
>+ *
>+ * Set up the device dplls, register them and pins connected within Linux dpll
>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
>+ * configuration requests.
>+ *
>+ * Context: Function initializes and holds pf->dplls.lock mutex.

No, it does not hold it.


>+ */
>+void ice_dpll_init(struct ice_pf *pf)
>+{
>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>+	struct ice_dplls *d = &pf->dplls;
>+	int err = 0;
>+
>+	err = ice_dpll_init_info(pf, cgu);
>+	if (err)
>+		goto err_exit;
>+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
>+	if (err)
>+		goto deinit_info;
>+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
>+	if (err)
>+		goto deinit_eec;
>+	err = ice_dpll_init_pins(pf, cgu);
>+	if (err)
>+		goto deinit_pps;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	if (cgu) {
>+		err = ice_dpll_init_worker(pf);
>+		if (err)
>+			goto deinit_pins;
>+	}
>+
>+	return;
>+
>+deinit_pins:
>+	ice_dpll_deinit_pins(pf, cgu);
>+deinit_pps:
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>+deinit_eec:
>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>+deinit_info:
>+	ice_dpll_deinit_info(pf);
>+err_exit:
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	mutex_unlock(&d->lock);

Leftover, please remove.


>+	mutex_destroy(&d->lock);
>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,104 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#ifndef _ICE_DPLL_H_
>+#define _ICE_DPLL_H_
>+
>+#include "ice.h"
>+
>+#define ICE_DPLL_PRIO_MAX	0xF
>+#define ICE_DPLL_RCLK_NUM_MAX	4
>+
>+/** ice_dpll_pin - store info about pins
>+ * @pin: dpll pin structure
>+ * @pf: pointer to pf, which has registered the dpll_pin
>+ * @idx: ice pin private idx
>+ * @num_parents: hols number of parent pins
>+ * @parent_idx: hold indexes of parent pins
>+ * @flags: pin flags returned from HW
>+ * @state: state of a pin
>+ * @prop: pin properities
>+ * @freq: current frequency of a pin
>+ */
>+struct ice_dpll_pin {
>+	struct dpll_pin *pin;
>+	struct ice_pf *pf;
>+	u8 idx;
>+	u8 num_parents;
>+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>+	struct dpll_pin_properties prop;
>+	u32 freq;
>+};
>+
>+/** ice_dpll - store info required for DPLL control
>+ * @dpll: pointer to dpll dev
>+ * @pf: pointer to pf, which has registered the dpll_device
>+ * @dpll_idx: index of dpll on the NIC
>+ * @input_idx: currently selected input index
>+ * @prev_input_idx: previously selected input index
>+ * @ref_state: state of dpll reference signals
>+ * @eec_mode: eec_mode dpll is configured for
>+ * @phase_shift: phase shift delay of a dpll
>+ * @input_prio: priorities of each input
>+ * @dpll_state: current dpll sync state
>+ * @prev_dpll_state: last dpll sync state
>+ * @active_input: pointer to active input pin
>+ * @prev_input: pointer to previous active input pin
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	struct ice_pf *pf;
>+	u8 dpll_idx;
>+	u8 input_idx;
>+	u8 prev_input_idx;
>+	u8 ref_state;
>+	u8 eec_mode;
>+	s64 phase_shift;
>+	u8 *input_prio;
>+	enum dpll_lock_status dpll_state;
>+	enum dpll_lock_status prev_dpll_state;
>+	enum dpll_mode mode;
>+	struct dpll_pin *active_input;
>+	struct dpll_pin *prev_input;
>+};
>+
>+/** ice_dplls - store info required for CCU (clock controlling unit)
>+ * @kworker: periodic worker
>+ * @work: periodic work
>+ * @lock: locks access to configuration of a dpll
>+ * @eec: pointer to EEC dpll dev
>+ * @pps: pointer to PPS dpll dev
>+ * @inputs: input pins pointer
>+ * @outputs: output pins pointer
>+ * @rclk: recovered pins pointer
>+ * @num_inputs: number of input pins available on dpll
>+ * @num_outputs: number of output pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>+ * @clock_id: clock_id of dplls
>+ */
>+struct ice_dplls {
>+	struct kthread_worker *kworker;
>+	struct kthread_delayed_work work;
>+	struct mutex lock;
>+	struct ice_dpll eec;
>+	struct ice_dpll pps;
>+	struct ice_dpll_pin *inputs;
>+	struct ice_dpll_pin *outputs;
>+	struct ice_dpll_pin rclk;
>+	u8 num_inputs;
>+	u8 num_outputs;
>+	int cgu_state_acq_err_num;
>+	u8 base_rclk_idx;
>+	u64 clock_id;
>+	s32 input_phase_adj_max;
>+	s32 output_phase_adj_max;
>+};
>+
>+void ice_dpll_init(struct ice_pf *pf);
>+
>+void ice_dpll_deinit(struct ice_pf *pf);
>+
>+#endif
>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
>index 19a5e7f3a075..0a94daaf3d20 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4613,6 +4613,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");
>@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
> 		ice_gnss_exit(pf);
> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
> 		ice_ptp_release(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>+	    ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_deinit(pf);
> }
> 
> static void ice_init_wakeup(struct ice_pf *pf)
>-- 
>2.27.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] 109+ messages in thread

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 11:17           ` Kubalewski, Arkadiusz
@ 2023-07-21 12:02             ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 12:02 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 9:33 AM
>>
>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>
>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>+/**
>>>>>+ * 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
>>>>>+ * @extack: error reporting
>>>>>+ *
>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>+ *
>>>>>+ * Context: Called under pf->dplls.lock
>>>>>+ * Return:
>>>>>+ * * 0 - OK
>>>>>+ * * negative - error
>>>>>+ */
>>>>>+static int
>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>+		    struct netlink_ext_ack *extack)
>>>>>+{
>>>>>+	u8 flags = 0;
>>>>>+	int ret;
>>>>>+
>>>>
>>>>
>>>>
>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>
>>>
>>>Because you are probably still thinking the modes are somehow connected
>>>to the state of the pin, but it is the other way around.
>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>any of the pins.
>>>
>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>patchset any description about why we need the freerun mode. What is
>>>>diffrerent between:
>>>>1) freerun mode
>>>>2) automatic mode & all pins disabled?
>>>
>>>The difference:
>>>Case I:
>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>AUTOMATIC
>>>2. switch to AUTOMATIC
>>>3. connecting to the valid source takes ~50 seconds
>>>
>>>Case II:
>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>2. switch one valid source to SELECTABLE
>>>3. connecting to the valid source takes ~10 seconds
>>>
>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>so in the end process of synchronizing with the source takes much longer as
>>>dpll need to start the process from scratch.
>>
>>I believe this is implementation detail of your HW. How you do it is up
>>to you. User does not have any visibility to this behaviour, therefore
>>makes no sense to expose UAPI that is considering it. Please drop it at
>>least for the initial patchset version. If you really need it later on
>>(which I honestly doubt), you can send it as a follow-up patchset.
>>
>
>And we will have the same discussion later.. But implementation is already
>there.

Yeah, it wouldn't block the initial submission. I would like to see this
merged, so anything which is blocking us and is totally optional (as
this freerun mode) is better to be dropped.


>As said in our previous discussion, without mode_set there is no point to have
>command DEVICE_SET at all, and there you said that you are ok with having the
>command as a placeholder, which doesn't make sense, since it is not used. 

I don't see any problem in having enum value reserved. But it does not
need to be there at all. You can add it to the end of the list when
needed. No problem. This is not an argument.


>
>Also this is not HW implementation detail but a synchronizer chip feature,
>once dpll is in FREERUN mode, the measurements like phase offset between the
>input and dpll's output won't be available.
>
>For the user there is a difference..
>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>where disconnecting sources is not, as they are still used, monitored and
>measured.

So it is not a mode! Mode is either "automatic" or "manual". Then we
have a state to indicate the state of the state machine (unlocked, locked,
holdover, holdover-acq). So what you seek is a way for the user to
expliticly set the state to "unlocked" and reset of the state machine.

Please don't mix config and state. I think we untangled this in the past
:/

Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
to hit this button.



>So probably most important fact that you are missing here: assuming the user
>disconnects the pin that dpll was locked with, our dpll doesn't go into UNLOCKED
>state but into HOLDOVER.
>
>>
>>
>>>
>>>>
>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>needs to be documented, please.
>>>>
>>>
>>>Sure will add the description of FREERUN to the docs.
>>
>>No, please drop it from this patchset. I have no clue why you readded
>>it in the first place in the last patchset version.
>>
>
>mode_set was there from the very beginning.. now implemented in ice driver
>as it should.

I don't understand the fixation on a callback to be implemented. Just
remove it. It can be easily added when needed. No problem.


>
>>
>>>
>>>>
>>>>
>>>>Another question, I asked the last time as well, but was not heard:
>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>connected with a single DPLL pin:
>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>
>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>
>>>>Could you please describe following 2 flows?
>>>>
>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>
>>>>
>>>>For mlx5 it goes like:
>>>>
>>>>DPLL device mode is MANUAL.
>>>>1)
>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>    -> pin_id: 10
>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>    -> device_id: 2
>>>
>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>relate to the dpll interface..
>>
>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>You need to set the state on a pin on a certain DPLL device.
>>
>
>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>know already something about the dpll it is managing.
>Not saying it is not needed, I am saying this is not a moment the SyncE daemon
>learns it.

Moment or not, it is needed for the cmd, that is why I have it there.


>But let's park it, as this is not really relevant.

Agreed.


>
>>
>>>
>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>CONNECTED
>>>>
>>>>2)
>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>    -> pin_id: 11
>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>    -> device_id: 2
>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>CONNECTED
>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>
>>>
>>>This flow is similar for ice, but there are some differences, although
>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>clock pins which are not directly connected to a dpll (connected through
>>>the MUX pin).
>>>
>>>1)
>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>pin_id: 13
>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>   (in case of dpll_id is needed, would be find in this response also)
>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>>   other pins shall be lower prio i.e. pin-prio:1)
>>
>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>state on DPLL device.
>>
>>
>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>   parent pin (pin-id:2)
>>
>>For this you need pin_id and pin_parent_id because you set the state on
>>a parent pin.
>>
>>
>>Yeah, this is exactly why I initially was in favour of hiding all the
>>muxes and magic around it hidden from the user. Now every userspace app
>>working with this has to implement a logic of tracking pin and the mux
>>parents (possibly multiple levels) and configure everything. But it just
>>need a simple thing: "select this pin as a source" :/
>>
>>
>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>in UAPI you were against in the past? Am I missing something?
>>
>
>Multiple level of muxes possibly could be hidden in the driver, but the fact
>they exist is not possible to be hidden from the user if the DPLL is in
>AUTOMATIC mode.
>For MANUAL mode dpll the muxes could be also hidden.
>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>pin.

Sure, but does user care how complicated things are inside? The syncE
daemon just cares for: "select netdev x as a source". However it is done
internally is irrelevant to him. With the existing UAPI, the syncE
daemon needs to learn individual device dpll/pin/mux topology and
work with it.

Do we need a dpll library to do this magic?


>
>Thank you!
>Arkadiusz
>
>>
>>
>>>
>>>2) (basically the same, only eth1 would get different pin_id.)
>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>pin_id: 14
>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>   parent pin (pin-id:2)
>>>
>>>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>>>phy recovery clock pin being connected through the MUX type pin.
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>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] 109+ messages in thread

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 12:02             ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 12:02 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 9:33 AM
>>
>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>
>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>+/**
>>>>>+ * 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
>>>>>+ * @extack: error reporting
>>>>>+ *
>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>+ *
>>>>>+ * Context: Called under pf->dplls.lock
>>>>>+ * Return:
>>>>>+ * * 0 - OK
>>>>>+ * * negative - error
>>>>>+ */
>>>>>+static int
>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>+		    struct netlink_ext_ack *extack)
>>>>>+{
>>>>>+	u8 flags = 0;
>>>>>+	int ret;
>>>>>+
>>>>
>>>>
>>>>
>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>
>>>
>>>Because you are probably still thinking the modes are somehow connected
>>>to the state of the pin, but it is the other way around.
>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>any of the pins.
>>>
>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>patchset any description about why we need the freerun mode. What is
>>>>diffrerent between:
>>>>1) freerun mode
>>>>2) automatic mode & all pins disabled?
>>>
>>>The difference:
>>>Case I:
>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>AUTOMATIC
>>>2. switch to AUTOMATIC
>>>3. connecting to the valid source takes ~50 seconds
>>>
>>>Case II:
>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>2. switch one valid source to SELECTABLE
>>>3. connecting to the valid source takes ~10 seconds
>>>
>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>so in the end process of synchronizing with the source takes much longer as
>>>dpll need to start the process from scratch.
>>
>>I believe this is implementation detail of your HW. How you do it is up
>>to you. User does not have any visibility to this behaviour, therefore
>>makes no sense to expose UAPI that is considering it. Please drop it at
>>least for the initial patchset version. If you really need it later on
>>(which I honestly doubt), you can send it as a follow-up patchset.
>>
>
>And we will have the same discussion later.. But implementation is already
>there.

Yeah, it wouldn't block the initial submission. I would like to see this
merged, so anything which is blocking us and is totally optional (as
this freerun mode) is better to be dropped.


>As said in our previous discussion, without mode_set there is no point to have
>command DEVICE_SET at all, and there you said that you are ok with having the
>command as a placeholder, which doesn't make sense, since it is not used. 

I don't see any problem in having enum value reserved. But it does not
need to be there at all. You can add it to the end of the list when
needed. No problem. This is not an argument.


>
>Also this is not HW implementation detail but a synchronizer chip feature,
>once dpll is in FREERUN mode, the measurements like phase offset between the
>input and dpll's output won't be available.
>
>For the user there is a difference..
>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>where disconnecting sources is not, as they are still used, monitored and
>measured.

So it is not a mode! Mode is either "automatic" or "manual". Then we
have a state to indicate the state of the state machine (unlocked, locked,
holdover, holdover-acq). So what you seek is a way for the user to
expliticly set the state to "unlocked" and reset of the state machine.

Please don't mix config and state. I think we untangled this in the past
:/

Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
to hit this button.



>So probably most important fact that you are missing here: assuming the user
>disconnects the pin that dpll was locked with, our dpll doesn't go into UNLOCKED
>state but into HOLDOVER.
>
>>
>>
>>>
>>>>
>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>needs to be documented, please.
>>>>
>>>
>>>Sure will add the description of FREERUN to the docs.
>>
>>No, please drop it from this patchset. I have no clue why you readded
>>it in the first place in the last patchset version.
>>
>
>mode_set was there from the very beginning.. now implemented in ice driver
>as it should.

I don't understand the fixation on a callback to be implemented. Just
remove it. It can be easily added when needed. No problem.


>
>>
>>>
>>>>
>>>>
>>>>Another question, I asked the last time as well, but was not heard:
>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>connected with a single DPLL pin:
>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>
>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>
>>>>Could you please describe following 2 flows?
>>>>
>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>
>>>>
>>>>For mlx5 it goes like:
>>>>
>>>>DPLL device mode is MANUAL.
>>>>1)
>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>    -> pin_id: 10
>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>    -> device_id: 2
>>>
>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>relate to the dpll interface..
>>
>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>You need to set the state on a pin on a certain DPLL device.
>>
>
>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>know already something about the dpll it is managing.
>Not saying it is not needed, I am saying this is not a moment the SyncE daemon
>learns it.

Moment or not, it is needed for the cmd, that is why I have it there.


>But let's park it, as this is not really relevant.

Agreed.


>
>>
>>>
>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>CONNECTED
>>>>
>>>>2)
>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>    -> pin_id: 11
>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>    -> device_id: 2
>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>CONNECTED
>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>
>>>
>>>This flow is similar for ice, but there are some differences, although
>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>clock pins which are not directly connected to a dpll (connected through
>>>the MUX pin).
>>>
>>>1)
>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>pin_id: 13
>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>   (in case of dpll_id is needed, would be find in this response also)
>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>>   other pins shall be lower prio i.e. pin-prio:1)
>>
>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>state on DPLL device.
>>
>>
>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>   parent pin (pin-id:2)
>>
>>For this you need pin_id and pin_parent_id because you set the state on
>>a parent pin.
>>
>>
>>Yeah, this is exactly why I initially was in favour of hiding all the
>>muxes and magic around it hidden from the user. Now every userspace app
>>working with this has to implement a logic of tracking pin and the mux
>>parents (possibly multiple levels) and configure everything. But it just
>>need a simple thing: "select this pin as a source" :/
>>
>>
>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>in UAPI you were against in the past? Am I missing something?
>>
>
>Multiple level of muxes possibly could be hidden in the driver, but the fact
>they exist is not possible to be hidden from the user if the DPLL is in
>AUTOMATIC mode.
>For MANUAL mode dpll the muxes could be also hidden.
>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>pin.

Sure, but does user care how complicated things are inside? The syncE
daemon just cares for: "select netdev x as a source". However it is done
internally is irrelevant to him. With the existing UAPI, the syncE
daemon needs to learn individual device dpll/pin/mux topology and
work with it.

Do we need a dpll library to do this magic?


>
>Thank you!
>Arkadiusz
>
>>
>>
>>>
>>>2) (basically the same, only eth1 would get different pin_id.)
>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>pin_id: 14
>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while all the
>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>   parent pin (pin-id:2)
>>>
>>>Where step c) is required due to AUTOMATIC mode, and step d) required due to
>>>phy recovery clock pin being connected through the MUX type pin.
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>Thanks!
>>>>
>>>>
>>>>[...]
>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 12:02             ` Jiri Pirko
@ 2023-07-21 13:36               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 13:36 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 2:02 PM
>
>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 9:33 AM
>>>
>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>
>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>+/**
>>>>>>+ * 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
>>>>>>+ * @extack: error reporting
>>>>>>+ *
>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>+ *
>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>+ * Return:
>>>>>>+ * * 0 - OK
>>>>>>+ * * negative - error
>>>>>>+ */
>>>>>>+static int
>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>+{
>>>>>>+	u8 flags = 0;
>>>>>>+	int ret;
>>>>>>+
>>>>>
>>>>>
>>>>>
>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>
>>>>
>>>>Because you are probably still thinking the modes are somehow connected
>>>>to the state of the pin, but it is the other way around.
>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>>any of the pins.
>>>>
>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>patchset any description about why we need the freerun mode. What is
>>>>>diffrerent between:
>>>>>1) freerun mode
>>>>>2) automatic mode & all pins disabled?
>>>>
>>>>The difference:
>>>>Case I:
>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>AUTOMATIC
>>>>2. switch to AUTOMATIC
>>>>3. connecting to the valid source takes ~50 seconds
>>>>
>>>>Case II:
>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>2. switch one valid source to SELECTABLE
>>>>3. connecting to the valid source takes ~10 seconds
>>>>
>>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>>so in the end process of synchronizing with the source takes much longer as
>>>>dpll need to start the process from scratch.
>>>
>>>I believe this is implementation detail of your HW. How you do it is up
>>>to you. User does not have any visibility to this behaviour, therefore
>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>least for the initial patchset version. If you really need it later on
>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>
>>
>>And we will have the same discussion later.. But implementation is already
>>there.
>
>Yeah, it wouldn't block the initial submission. I would like to see this
>merged, so anything which is blocking us and is totally optional (as
>this freerun mode) is better to be dropped.
>

It is not blocking anything. Most of it was defined and available for
long time already. Only ice implementing set_mode is a new part.
No clue what is the problem you are implying here.

>
>>As said in our previous discussion, without mode_set there is no point to
>>have
>>command DEVICE_SET at all, and there you said that you are ok with having
>>the
>>command as a placeholder, which doesn't make sense, since it is not used.
>
>I don't see any problem in having enum value reserved. But it does not
>need to be there at all. You can add it to the end of the list when
>needed. No problem. This is not an argument.
>

The argument is that I already implemented and tested, and have the need for the
existence to set_mode to configure DPLL, which is there to switch the mode
between AUTOMATIC and FREERUN.

>
>>
>>Also this is not HW implementation detail but a synchronizer chip feature,
>>once dpll is in FREERUN mode, the measurements like phase offset between
>>the
>>input and dpll's output won't be available.
>>
>>For the user there is a difference..
>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>where disconnecting sources is not, as they are still used, monitored and
>>measured.
>
>So it is not a mode! Mode is either "automatic" or "manual". Then we
>have a state to indicate the state of the state machine (unlocked, locked,
>holdover, holdover-acq). So what you seek is a way for the user to
>expliticly set the state to "unlocked" and reset of the state machine.
>
>Please don't mix config and state. I think we untangled this in the past
>:/

I don't mix anything, this is the way dpll works, which means mode of dpll.

>
>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>to hit this button.
>

As already said there are measurement in place in AUTOMATIC, there are no such
thing in FREERUN. Going into FREERUN resets the state machine of dpll which
is a side effect of going to FREERUN.

>
>
>>So probably most important fact that you are missing here: assuming the user
>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>UNLOCKED
>>state but into HOLDOVER.
>>
>>>
>>>
>>>>
>>>>>
>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>needs to be documented, please.
>>>>>
>>>>
>>>>Sure will add the description of FREERUN to the docs.
>>>
>>>No, please drop it from this patchset. I have no clue why you readded
>>>it in the first place in the last patchset version.
>>>
>>
>>mode_set was there from the very beginning.. now implemented in ice driver
>>as it should.
>
>I don't understand the fixation on a callback to be implemented. Just
>remove it. It can be easily added when needed. No problem.
>

Well, I don't understand the fixation about removing it.
set_mode was there for a long time, now the callback is properly implemented
and you are trying to imply that this is not needed.
We require it, as there is no other other way to stop AUTOMATIC mode dpll
to do its work.

>
>>
>>>
>>>>
>>>>>
>>>>>
>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>connected with a single DPLL pin:
>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>
>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>
>>>>>Could you please describe following 2 flows?
>>>>>
>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>
>>>>>
>>>>>For mlx5 it goes like:
>>>>>
>>>>>DPLL device mode is MANUAL.
>>>>>1)
>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>    -> pin_id: 10
>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>    -> device_id: 2
>>>>
>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>relate to the dpll interface..
>>>
>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>You need to set the state on a pin on a certain DPLL device.
>>>
>>
>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>know already something about the dpll it is managing.
>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>daemon
>>learns it.
>
>Moment or not, it is needed for the cmd, that is why I have it there.
>
>
>>But let's park it, as this is not really relevant.
>
>Agreed.
>
>
>>
>>>
>>>>
>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>CONNECTED
>>>>>
>>>>>2)
>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>    -> pin_id: 11
>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>    -> device_id: 2
>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>CONNECTED
>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>
>>>>
>>>>This flow is similar for ice, but there are some differences, although
>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>clock pins which are not directly connected to a dpll (connected through
>>>>the MUX pin).
>>>>
>>>>1)
>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>pin_id: 13
>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>all the
>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>
>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>state on DPLL device.
>>>
>>>
>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>>   parent pin (pin-id:2)
>>>
>>>For this you need pin_id and pin_parent_id because you set the state on
>>>a parent pin.
>>>
>>>
>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>muxes and magic around it hidden from the user. Now every userspace app
>>>working with this has to implement a logic of tracking pin and the mux
>>>parents (possibly multiple levels) and configure everything. But it just
>>>need a simple thing: "select this pin as a source" :/
>>>
>>>
>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>in UAPI you were against in the past? Am I missing something?
>>>
>>
>>Multiple level of muxes possibly could be hidden in the driver, but the fact
>>they exist is not possible to be hidden from the user if the DPLL is in
>>AUTOMATIC mode.
>>For MANUAL mode dpll the muxes could be also hidden.
>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>>pin.
>
>Sure, but does user care how complicated things are inside? The syncE
>daemon just cares for: "select netdev x as a source". However it is done
>internally is irrelevant to him. With the existing UAPI, the syncE
>daemon needs to learn individual device dpll/pin/mux topology and
>work with it.
>

This is dpll subsystem not SyncE one.

>Do we need a dpll library to do this magic?
>

IMHO rather SyncE library :)

Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>
>>>>
>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>pin_id: 14
>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>all the
>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>>   parent pin (pin-id:2)
>>>>
>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>due to
>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>Thanks!
>>>>>
>>>>>
>>>>>[...]
>>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 13:36               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 13:36 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 2:02 PM
>
>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 9:33 AM
>>>
>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>
>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>+/**
>>>>>>+ * 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
>>>>>>+ * @extack: error reporting
>>>>>>+ *
>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>+ *
>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>+ * Return:
>>>>>>+ * * 0 - OK
>>>>>>+ * * negative - error
>>>>>>+ */
>>>>>>+static int
>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>+{
>>>>>>+	u8 flags = 0;
>>>>>>+	int ret;
>>>>>>+
>>>>>
>>>>>
>>>>>
>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>
>>>>
>>>>Because you are probably still thinking the modes are somehow connected
>>>>to the state of the pin, but it is the other way around.
>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>>any of the pins.
>>>>
>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>patchset any description about why we need the freerun mode. What is
>>>>>diffrerent between:
>>>>>1) freerun mode
>>>>>2) automatic mode & all pins disabled?
>>>>
>>>>The difference:
>>>>Case I:
>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>AUTOMATIC
>>>>2. switch to AUTOMATIC
>>>>3. connecting to the valid source takes ~50 seconds
>>>>
>>>>Case II:
>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>2. switch one valid source to SELECTABLE
>>>>3. connecting to the valid source takes ~10 seconds
>>>>
>>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>>so in the end process of synchronizing with the source takes much longer as
>>>>dpll need to start the process from scratch.
>>>
>>>I believe this is implementation detail of your HW. How you do it is up
>>>to you. User does not have any visibility to this behaviour, therefore
>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>least for the initial patchset version. If you really need it later on
>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>
>>
>>And we will have the same discussion later.. But implementation is already
>>there.
>
>Yeah, it wouldn't block the initial submission. I would like to see this
>merged, so anything which is blocking us and is totally optional (as
>this freerun mode) is better to be dropped.
>

It is not blocking anything. Most of it was defined and available for
long time already. Only ice implementing set_mode is a new part.
No clue what is the problem you are implying here.

>
>>As said in our previous discussion, without mode_set there is no point to
>>have
>>command DEVICE_SET at all, and there you said that you are ok with having
>>the
>>command as a placeholder, which doesn't make sense, since it is not used.
>
>I don't see any problem in having enum value reserved. But it does not
>need to be there at all. You can add it to the end of the list when
>needed. No problem. This is not an argument.
>

The argument is that I already implemented and tested, and have the need for the
existence to set_mode to configure DPLL, which is there to switch the mode
between AUTOMATIC and FREERUN.

>
>>
>>Also this is not HW implementation detail but a synchronizer chip feature,
>>once dpll is in FREERUN mode, the measurements like phase offset between
>>the
>>input and dpll's output won't be available.
>>
>>For the user there is a difference..
>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>where disconnecting sources is not, as they are still used, monitored and
>>measured.
>
>So it is not a mode! Mode is either "automatic" or "manual". Then we
>have a state to indicate the state of the state machine (unlocked, locked,
>holdover, holdover-acq). So what you seek is a way for the user to
>expliticly set the state to "unlocked" and reset of the state machine.
>
>Please don't mix config and state. I think we untangled this in the past
>:/

I don't mix anything, this is the way dpll works, which means mode of dpll.

>
>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>to hit this button.
>

As already said there are measurement in place in AUTOMATIC, there are no such
thing in FREERUN. Going into FREERUN resets the state machine of dpll which
is a side effect of going to FREERUN.

>
>
>>So probably most important fact that you are missing here: assuming the user
>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>UNLOCKED
>>state but into HOLDOVER.
>>
>>>
>>>
>>>>
>>>>>
>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>needs to be documented, please.
>>>>>
>>>>
>>>>Sure will add the description of FREERUN to the docs.
>>>
>>>No, please drop it from this patchset. I have no clue why you readded
>>>it in the first place in the last patchset version.
>>>
>>
>>mode_set was there from the very beginning.. now implemented in ice driver
>>as it should.
>
>I don't understand the fixation on a callback to be implemented. Just
>remove it. It can be easily added when needed. No problem.
>

Well, I don't understand the fixation about removing it.
set_mode was there for a long time, now the callback is properly implemented
and you are trying to imply that this is not needed.
We require it, as there is no other other way to stop AUTOMATIC mode dpll
to do its work.

>
>>
>>>
>>>>
>>>>>
>>>>>
>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>connected with a single DPLL pin:
>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>
>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>
>>>>>Could you please describe following 2 flows?
>>>>>
>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>
>>>>>
>>>>>For mlx5 it goes like:
>>>>>
>>>>>DPLL device mode is MANUAL.
>>>>>1)
>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>    -> pin_id: 10
>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>    -> device_id: 2
>>>>
>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>relate to the dpll interface..
>>>
>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>You need to set the state on a pin on a certain DPLL device.
>>>
>>
>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>know already something about the dpll it is managing.
>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>daemon
>>learns it.
>
>Moment or not, it is needed for the cmd, that is why I have it there.
>
>
>>But let's park it, as this is not really relevant.
>
>Agreed.
>
>
>>
>>>
>>>>
>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>CONNECTED
>>>>>
>>>>>2)
>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>    -> pin_id: 11
>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>    -> device_id: 2
>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>CONNECTED
>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>
>>>>
>>>>This flow is similar for ice, but there are some differences, although
>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>clock pins which are not directly connected to a dpll (connected through
>>>>the MUX pin).
>>>>
>>>>1)
>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>pin_id: 13
>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>all the
>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>
>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>state on DPLL device.
>>>
>>>
>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>>   parent pin (pin-id:2)
>>>
>>>For this you need pin_id and pin_parent_id because you set the state on
>>>a parent pin.
>>>
>>>
>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>muxes and magic around it hidden from the user. Now every userspace app
>>>working with this has to implement a logic of tracking pin and the mux
>>>parents (possibly multiple levels) and configure everything. But it just
>>>need a simple thing: "select this pin as a source" :/
>>>
>>>
>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>in UAPI you were against in the past? Am I missing something?
>>>
>>
>>Multiple level of muxes possibly could be hidden in the driver, but the fact
>>they exist is not possible to be hidden from the user if the DPLL is in
>>AUTOMATIC mode.
>>For MANUAL mode dpll the muxes could be also hidden.
>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>>pin.
>
>Sure, but does user care how complicated things are inside? The syncE
>daemon just cares for: "select netdev x as a source". However it is done
>internally is irrelevant to him. With the existing UAPI, the syncE
>daemon needs to learn individual device dpll/pin/mux topology and
>work with it.
>

This is dpll subsystem not SyncE one.

>Do we need a dpll library to do this magic?
>

IMHO rather SyncE library :)

Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>
>>>>
>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>pin_id: 14
>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>all the
>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>>   parent pin (pin-id:2)
>>>>
>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>due to
>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>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] 109+ messages in thread

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
  2023-07-21 11:14   ` Jiri Pirko
@ 2023-07-21 14:42     ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-21 14:42 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On 21.07.2023 12:14, Jiri Pirko wrote:
> There are couple of issues that came up during our internal ci run:
> 
> 10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
> 10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
> 10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]
> 
> 10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
> 10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
> 10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
> 10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement
> 
> I believe that you didn't run make with C=2, otherwise you would hit
> these.

Yeah, I'll re-run the set patch-by-patch with C=2 next time.

> 
> Checkpatch issue:
> 10:29:30  CHECK: struct mutex definition without comment
> 10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
> 10:29:30  +	struct mutex lock;

Arkadiusz will take care of "ice" part.


> Spelling errors:
> 10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
> 10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties
> 

Will fix it.

> 
> Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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
> 
> s/API aim/API aims/
> 
> 
>> make it flexible and easy to cover special configurations.
> 
> I don't follow. How this could "aim to extend current pin configuration" ?
> This is a new thing. Could you re-phrase?

Not really new. PTP devices have already simple pin configurations, mlx5 is 
using it for some cards with external pins. The problem is that PTP subsystem
covers only simple configuration of the pin and doesn't cover DPLL part at all.

> 
> What's "special configuration"? Sounds odd.
> 

Yeah, "complex configurations" sounds better, will change it.

> 
>>
>> Netlink interface is based on ynl spec, it allows use of in-kernel
>> tools/net/ynl/cli.py application to control the interface with properly
>> formated command and json attribute strings. Here are few command
>> examples of how it works with `ice` driver on supported NIC:
>>
>> - dump dpll devices
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
> 
> "$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
> root is needed to perform the command.
> 
> 
>> --dump device-get
>> [{'clock-id': 282574471561216,
>>   'id': 0,
>>   'lock-status': 'unlocked',
>>   'mode': 'automatic',
>>   'module-name': 'ice',
>>   'type': 'eec'},
>> {'clock-id': 282574471561216,
>>   'id': 1,
>>   'lock-status': 'unlocked',
>>   'mode': 'automatic',
>>   'module-name': 'ice',
>>   'type': 'pps'}]
>>
>> - get single pin info:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-get --json '{"pin-id":2}'
>> {'clock-id': 282574471561216,
>> 'module-name': 'ice',
>> 'pin-board-label': 'C827_0-RCLKA',
>> 'pin-dpll-caps': 6,
>> 'pin-frequency': 1953125,
>> 'pin-id': 2,
>> 'pin-parenti-device': [{'id': 0,
> 
> This looks like manual edit went wrong :)
> s/parenti/parent/
> 

Ahhh... yeah :)

> 
>>                          'pin-direction': 'input',
>>                          'pin-prio': 11,
>>                          'pin-state': 'selectable'},
>>                         {'id': 1,
>>                          'pin-direction': 'input',
>>                          'pin-prio': 9,
>>                          'pin-state': 'selectable'}],
>> 'pin-type': 'mux'}
>>
>> - set pin's state on dpll:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>>
>> - set pin's prio on dpll:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>>
>> - set pin's state on parent pin:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":13, \
>>                       "pin-parent-pin":{"pin-id":2, "pin-state":1}}'
>>
> 
> [...]


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

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-21 14:42     ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-21 14:42 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On 21.07.2023 12:14, Jiri Pirko wrote:
> There are couple of issues that came up during our internal ci run:
> 
> 10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
> 10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
> 10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]
> 
> 10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
> 10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
> 10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
> 10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement
> 
> I believe that you didn't run make with C=2, otherwise you would hit
> these.

Yeah, I'll re-run the set patch-by-patch with C=2 next time.

> 
> Checkpatch issue:
> 10:29:30  CHECK: struct mutex definition without comment
> 10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
> 10:29:30  +	struct mutex lock;

Arkadiusz will take care of "ice" part.


> Spelling errors:
> 10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
> 10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties
> 

Will fix it.

> 
> Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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
> 
> s/API aim/API aims/
> 
> 
>> make it flexible and easy to cover special configurations.
> 
> I don't follow. How this could "aim to extend current pin configuration" ?
> This is a new thing. Could you re-phrase?

Not really new. PTP devices have already simple pin configurations, mlx5 is 
using it for some cards with external pins. The problem is that PTP subsystem
covers only simple configuration of the pin and doesn't cover DPLL part at all.

> 
> What's "special configuration"? Sounds odd.
> 

Yeah, "complex configurations" sounds better, will change it.

> 
>>
>> Netlink interface is based on ynl spec, it allows use of in-kernel
>> tools/net/ynl/cli.py application to control the interface with properly
>> formated command and json attribute strings. Here are few command
>> examples of how it works with `ice` driver on supported NIC:
>>
>> - dump dpll devices
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
> 
> "$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
> root is needed to perform the command.
> 
> 
>> --dump device-get
>> [{'clock-id': 282574471561216,
>>   'id': 0,
>>   'lock-status': 'unlocked',
>>   'mode': 'automatic',
>>   'module-name': 'ice',
>>   'type': 'eec'},
>> {'clock-id': 282574471561216,
>>   'id': 1,
>>   'lock-status': 'unlocked',
>>   'mode': 'automatic',
>>   'module-name': 'ice',
>>   'type': 'pps'}]
>>
>> - get single pin info:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-get --json '{"pin-id":2}'
>> {'clock-id': 282574471561216,
>> 'module-name': 'ice',
>> 'pin-board-label': 'C827_0-RCLKA',
>> 'pin-dpll-caps': 6,
>> 'pin-frequency': 1953125,
>> 'pin-id': 2,
>> 'pin-parenti-device': [{'id': 0,
> 
> This looks like manual edit went wrong :)
> s/parenti/parent/
> 

Ahhh... yeah :)

> 
>>                          'pin-direction': 'input',
>>                          'pin-prio': 11,
>>                          'pin-state': 'selectable'},
>>                         {'id': 1,
>>                          'pin-direction': 'input',
>>                          'pin-prio': 9,
>>                          'pin-state': 'selectable'}],
>> 'pin-type': 'mux'}
>>
>> - set pin's state on dpll:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>>
>> - set pin's prio on dpll:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>>
>> - set pin's state on parent pin:
>> $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> --do pin-set --json '{"pin-id":13, \
>>                       "pin-parent-pin":{"pin-id":2, "pin-state":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] 109+ messages in thread

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 13:36               ` Kubalewski, Arkadiusz
@ 2023-07-21 15:45                 ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:45 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 2:02 PM
>>
>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>
>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>
>>>>>>[...]
>>>>>>
>>>>>>
>>>>>>>+/**
>>>>>>>+ * 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
>>>>>>>+ * @extack: error reporting
>>>>>>>+ *
>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>+ *
>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>+ * Return:
>>>>>>>+ * * 0 - OK
>>>>>>>+ * * negative - error
>>>>>>>+ */
>>>>>>>+static int
>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>+{
>>>>>>>+	u8 flags = 0;
>>>>>>>+	int ret;
>>>>>>>+
>>>>>>
>>>>>>
>>>>>>
>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>
>>>>>
>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>to the state of the pin, but it is the other way around.
>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>>>any of the pins.
>>>>>
>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>diffrerent between:
>>>>>>1) freerun mode
>>>>>>2) automatic mode & all pins disabled?
>>>>>
>>>>>The difference:
>>>>>Case I:
>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>AUTOMATIC
>>>>>2. switch to AUTOMATIC
>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>
>>>>>Case II:
>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>2. switch one valid source to SELECTABLE
>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>
>>>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>>>so in the end process of synchronizing with the source takes much longer as
>>>>>dpll need to start the process from scratch.
>>>>
>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>least for the initial patchset version. If you really need it later on
>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>
>>>
>>>And we will have the same discussion later.. But implementation is already
>>>there.
>>
>>Yeah, it wouldn't block the initial submission. I would like to see this
>>merged, so anything which is blocking us and is totally optional (as
>>this freerun mode) is better to be dropped.
>>
>
>It is not blocking anything. Most of it was defined and available for
>long time already. Only ice implementing set_mode is a new part.
>No clue what is the problem you are implying here.

Problem is that I believe you freerun mode should not exist. I believe
it is wrong.


>
>>
>>>As said in our previous discussion, without mode_set there is no point to
>>>have
>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>the
>>>command as a placeholder, which doesn't make sense, since it is not used.
>>
>>I don't see any problem in having enum value reserved. But it does not
>>need to be there at all. You can add it to the end of the list when
>>needed. No problem. This is not an argument.
>>
>
>The argument is that I already implemented and tested, and have the need for the
>existence to set_mode to configure DPLL, which is there to switch the mode
>between AUTOMATIC and FREERUN.
>
>>
>>>
>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>the
>>>input and dpll's output won't be available.
>>>
>>>For the user there is a difference..
>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>where disconnecting sources is not, as they are still used, monitored and
>>>measured.
>>
>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>have a state to indicate the state of the state machine (unlocked, locked,
>>holdover, holdover-acq). So what you seek is a way for the user to
>>expliticly set the state to "unlocked" and reset of the state machine.
>>
>>Please don't mix config and state. I think we untangled this in the past
>>:/
>
>I don't mix anything, this is the way dpll works, which means mode of dpll.

You do. You want to force-change the state yet you mangle the mode in.
The fact that some specific dpll implemented it as mode does not mean it
has to be exposed like that to user. We have to find the right
abstraction.


>
>>
>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>to hit this button.
>>
>
>As already said there are measurement in place in AUTOMATIC, there are no such
>thing in FREERUN. Going into FREERUN resets the state machine of dpll which
>is a side effect of going to FREERUN.
>
>>
>>
>>>So probably most important fact that you are missing here: assuming the user
>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>UNLOCKED
>>>state but into HOLDOVER.
>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>needs to be documented, please.
>>>>>>
>>>>>
>>>>>Sure will add the description of FREERUN to the docs.
>>>>
>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>it in the first place in the last patchset version.
>>>>
>>>
>>>mode_set was there from the very beginning.. now implemented in ice driver
>>>as it should.
>>
>>I don't understand the fixation on a callback to be implemented. Just
>>remove it. It can be easily added when needed. No problem.
>>
>
>Well, I don't understand the fixation about removing it.

It is needed only for your freerun mode, which is questionable. This
discussion it not about mode_set. I don't care about it, if it is
needed, should be there, if not, so be it.

As you say, you need existance of your freerun mode to justify existence
of mode_set(). Could you please, please drop both for now so we can
move on? I'm tired of this. Thanks!


>set_mode was there for a long time, now the callback is properly implemented
>and you are trying to imply that this is not needed.
>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>to do its work.
>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>connected with a single DPLL pin:
>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>
>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>
>>>>>>Could you please describe following 2 flows?
>>>>>>
>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>
>>>>>>
>>>>>>For mlx5 it goes like:
>>>>>>
>>>>>>DPLL device mode is MANUAL.
>>>>>>1)
>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>    -> pin_id: 10
>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>    -> device_id: 2
>>>>>
>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>relate to the dpll interface..
>>>>
>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>You need to set the state on a pin on a certain DPLL device.
>>>>
>>>
>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>know already something about the dpll it is managing.
>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>daemon
>>>learns it.
>>
>>Moment or not, it is needed for the cmd, that is why I have it there.
>>
>>
>>>But let's park it, as this is not really relevant.
>>
>>Agreed.
>>
>>
>>>
>>>>
>>>>>
>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>CONNECTED
>>>>>>
>>>>>>2)
>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>    -> pin_id: 11
>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>    -> device_id: 2
>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>CONNECTED
>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>
>>>>>
>>>>>This flow is similar for ice, but there are some differences, although
>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>the MUX pin).
>>>>>
>>>>>1)
>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>pin_id: 13
>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>all the
>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>
>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>state on DPLL device.
>>>>
>>>>
>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>>>   parent pin (pin-id:2)
>>>>
>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>a parent pin.
>>>>
>>>>
>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>working with this has to implement a logic of tracking pin and the mux
>>>>parents (possibly multiple levels) and configure everything. But it just
>>>>need a simple thing: "select this pin as a source" :/
>>>>
>>>>
>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>in UAPI you were against in the past? Am I missing something?
>>>>
>>>
>>>Multiple level of muxes possibly could be hidden in the driver, but the fact
>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>AUTOMATIC mode.
>>>For MANUAL mode dpll the muxes could be also hidden.
>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>>>pin.
>>
>>Sure, but does user care how complicated things are inside? The syncE
>>daemon just cares for: "select netdev x as a source". However it is done
>>internally is irrelevant to him. With the existing UAPI, the syncE
>>daemon needs to learn individual device dpll/pin/mux topology and
>>work with it.
>>
>
>This is dpll subsystem not SyncE one.

SyncE is very legit use case of the UAPI. I would say perhaps the most
important.


>
>>Do we need a dpll library to do this magic?
>>
>
>IMHO rather SyncE library :)
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>
>>>>>
>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>pin_id: 14
>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>all the
>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>>>   parent pin (pin-id:2)
>>>>>
>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>due to
>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>Thanks!
>>>>>>
>>>>>>
>>>>>>[...]
>>>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 15:45                 ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:45 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 2:02 PM
>>
>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>
>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>
>>>>>>[...]
>>>>>>
>>>>>>
>>>>>>>+/**
>>>>>>>+ * 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
>>>>>>>+ * @extack: error reporting
>>>>>>>+ *
>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>+ *
>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>+ * Return:
>>>>>>>+ * * 0 - OK
>>>>>>>+ * * negative - error
>>>>>>>+ */
>>>>>>>+static int
>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>+{
>>>>>>>+	u8 flags = 0;
>>>>>>>+	int ret;
>>>>>>>+
>>>>>>
>>>>>>
>>>>>>
>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>
>>>>>
>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>to the state of the pin, but it is the other way around.
>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or monitor
>>>>>any of the pins.
>>>>>
>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>diffrerent between:
>>>>>>1) freerun mode
>>>>>>2) automatic mode & all pins disabled?
>>>>>
>>>>>The difference:
>>>>>Case I:
>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>AUTOMATIC
>>>>>2. switch to AUTOMATIC
>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>
>>>>>Case II:
>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>2. switch one valid source to SELECTABLE
>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>
>>>>>Basically in AUTOMATIC mode the sources are still monitored even when they
>>>>>are not in SELECTABLE state, while in FREERUN there is no such monitoring,
>>>>>so in the end process of synchronizing with the source takes much longer as
>>>>>dpll need to start the process from scratch.
>>>>
>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>least for the initial patchset version. If you really need it later on
>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>
>>>
>>>And we will have the same discussion later.. But implementation is already
>>>there.
>>
>>Yeah, it wouldn't block the initial submission. I would like to see this
>>merged, so anything which is blocking us and is totally optional (as
>>this freerun mode) is better to be dropped.
>>
>
>It is not blocking anything. Most of it was defined and available for
>long time already. Only ice implementing set_mode is a new part.
>No clue what is the problem you are implying here.

Problem is that I believe you freerun mode should not exist. I believe
it is wrong.


>
>>
>>>As said in our previous discussion, without mode_set there is no point to
>>>have
>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>the
>>>command as a placeholder, which doesn't make sense, since it is not used.
>>
>>I don't see any problem in having enum value reserved. But it does not
>>need to be there at all. You can add it to the end of the list when
>>needed. No problem. This is not an argument.
>>
>
>The argument is that I already implemented and tested, and have the need for the
>existence to set_mode to configure DPLL, which is there to switch the mode
>between AUTOMATIC and FREERUN.
>
>>
>>>
>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>the
>>>input and dpll's output won't be available.
>>>
>>>For the user there is a difference..
>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>where disconnecting sources is not, as they are still used, monitored and
>>>measured.
>>
>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>have a state to indicate the state of the state machine (unlocked, locked,
>>holdover, holdover-acq). So what you seek is a way for the user to
>>expliticly set the state to "unlocked" and reset of the state machine.
>>
>>Please don't mix config and state. I think we untangled this in the past
>>:/
>
>I don't mix anything, this is the way dpll works, which means mode of dpll.

You do. You want to force-change the state yet you mangle the mode in.
The fact that some specific dpll implemented it as mode does not mean it
has to be exposed like that to user. We have to find the right
abstraction.


>
>>
>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>to hit this button.
>>
>
>As already said there are measurement in place in AUTOMATIC, there are no such
>thing in FREERUN. Going into FREERUN resets the state machine of dpll which
>is a side effect of going to FREERUN.
>
>>
>>
>>>So probably most important fact that you are missing here: assuming the user
>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>UNLOCKED
>>>state but into HOLDOVER.
>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>needs to be documented, please.
>>>>>>
>>>>>
>>>>>Sure will add the description of FREERUN to the docs.
>>>>
>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>it in the first place in the last patchset version.
>>>>
>>>
>>>mode_set was there from the very beginning.. now implemented in ice driver
>>>as it should.
>>
>>I don't understand the fixation on a callback to be implemented. Just
>>remove it. It can be easily added when needed. No problem.
>>
>
>Well, I don't understand the fixation about removing it.

It is needed only for your freerun mode, which is questionable. This
discussion it not about mode_set. I don't care about it, if it is
needed, should be there, if not, so be it.

As you say, you need existance of your freerun mode to justify existence
of mode_set(). Could you please, please drop both for now so we can
move on? I'm tired of this. Thanks!


>set_mode was there for a long time, now the callback is properly implemented
>and you are trying to imply that this is not needed.
>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>to do its work.
>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>connected with a single DPLL pin:
>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>
>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>
>>>>>>Could you please describe following 2 flows?
>>>>>>
>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>
>>>>>>
>>>>>>For mlx5 it goes like:
>>>>>>
>>>>>>DPLL device mode is MANUAL.
>>>>>>1)
>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>    -> pin_id: 10
>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>    -> device_id: 2
>>>>>
>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>relate to the dpll interface..
>>>>
>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>You need to set the state on a pin on a certain DPLL device.
>>>>
>>>
>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>know already something about the dpll it is managing.
>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>daemon
>>>learns it.
>>
>>Moment or not, it is needed for the cmd, that is why I have it there.
>>
>>
>>>But let's park it, as this is not really relevant.
>>
>>Agreed.
>>
>>
>>>
>>>>
>>>>>
>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>CONNECTED
>>>>>>
>>>>>>2)
>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>    -> pin_id: 11
>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>    -> device_id: 2
>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>CONNECTED
>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>
>>>>>
>>>>>This flow is similar for ice, but there are some differences, although
>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>the MUX pin).
>>>>>
>>>>>1)
>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>pin_id: 13
>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>all the
>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>
>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>state on DPLL device.
>>>>
>>>>
>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
>>>>>   parent pin (pin-id:2)
>>>>
>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>a parent pin.
>>>>
>>>>
>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>working with this has to implement a logic of tracking pin and the mux
>>>>parents (possibly multiple levels) and configure everything. But it just
>>>>need a simple thing: "select this pin as a source" :/
>>>>
>>>>
>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>in UAPI you were against in the past? Am I missing something?
>>>>
>>>
>>>Multiple level of muxes possibly could be hidden in the driver, but the fact
>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>AUTOMATIC mode.
>>>For MANUAL mode dpll the muxes could be also hidden.
>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED type
>>>pin.
>>
>>Sure, but does user care how complicated things are inside? The syncE
>>daemon just cares for: "select netdev x as a source". However it is done
>>internally is irrelevant to him. With the existing UAPI, the syncE
>>daemon needs to learn individual device dpll/pin/mux topology and
>>work with it.
>>
>
>This is dpll subsystem not SyncE one.

SyncE is very legit use case of the UAPI. I would say perhaps the most
important.


>
>>Do we need a dpll library to do this magic?
>>
>
>IMHO rather SyncE library :)
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>
>>>>>
>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>pin_id: 14
>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>all the
>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED with
>>>>>   parent pin (pin-id:2)
>>>>>
>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>due to
>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>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] 109+ messages in thread

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
  2023-07-21 14:42     ` Vadim Fedorenko
@ 2023-07-21 15:46       ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:46 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 04:42:46PM CEST, vadim.fedorenko@linux.dev wrote:
>On 21.07.2023 12:14, Jiri Pirko wrote:
>> There are couple of issues that came up during our internal ci run:
>> 
>> 10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
>> 10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
>> 10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]
>> 
>> 10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
>> 10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
>> 10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
>> 10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement
>> 
>> I believe that you didn't run make with C=2, otherwise you would hit
>> these.
>
>Yeah, I'll re-run the set patch-by-patch with C=2 next time.
>
>> 
>> Checkpatch issue:
>> 10:29:30  CHECK: struct mutex definition without comment
>> 10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
>> 10:29:30  +	struct mutex lock;
>
>Arkadiusz will take care of "ice" part.
>
>
>> Spelling errors:
>> 10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
>> 10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
>> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
>> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties
>> 
>
>Will fix it.
>
>> 
>> Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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
>> 
>> s/API aim/API aims/
>> 
>> 
>> > make it flexible and easy to cover special configurations.
>> 
>> I don't follow. How this could "aim to extend current pin configuration" ?
>> This is a new thing. Could you re-phrase?
>
>Not really new. PTP devices have already simple pin configurations, mlx5 is
>using it for some cards with external pins. The problem is that PTP subsystem
>covers only simple configuration of the pin and doesn't cover DPLL part at all.

Okay, please put the info in.


>
>> 
>> What's "special configuration"? Sounds odd.
>> 
>
>Yeah, "complex configurations" sounds better, will change it.
>
>> 
>> > 
>> > Netlink interface is based on ynl spec, it allows use of in-kernel
>> > tools/net/ynl/cli.py application to control the interface with properly
>> > formated command and json attribute strings. Here are few command
>> > examples of how it works with `ice` driver on supported NIC:
>> > 
>> > - dump dpll devices
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> 
>> "$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
>> root is needed to perform the command.
>> 
>> 
>> > --dump device-get
>> > [{'clock-id': 282574471561216,
>> >   'id': 0,
>> >   'lock-status': 'unlocked',
>> >   'mode': 'automatic',
>> >   'module-name': 'ice',
>> >   'type': 'eec'},
>> > {'clock-id': 282574471561216,
>> >   'id': 1,
>> >   'lock-status': 'unlocked',
>> >   'mode': 'automatic',
>> >   'module-name': 'ice',
>> >   'type': 'pps'}]
>> > 
>> > - get single pin info:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-get --json '{"pin-id":2}'
>> > {'clock-id': 282574471561216,
>> > 'module-name': 'ice',
>> > 'pin-board-label': 'C827_0-RCLKA',
>> > 'pin-dpll-caps': 6,
>> > 'pin-frequency': 1953125,
>> > 'pin-id': 2,
>> > 'pin-parenti-device': [{'id': 0,
>> 
>> This looks like manual edit went wrong :)
>> s/parenti/parent/
>> 
>
>Ahhh... yeah :)
>
>> 
>> >                          'pin-direction': 'input',
>> >                          'pin-prio': 11,
>> >                          'pin-state': 'selectable'},
>> >                         {'id': 1,
>> >                          'pin-direction': 'input',
>> >                          'pin-prio': 9,
>> >                          'pin-state': 'selectable'}],
>> > 'pin-type': 'mux'}
>> > 
>> > - set pin's state on dpll:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>> > 
>> > - set pin's prio on dpll:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>> > 
>> > - set pin's state on parent pin:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":13, \
>> >                       "pin-parent-pin":{"pin-id":2, "pin-state":1}}'
>> > 
>> 
>> [...]
>

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

* Re: [PATCH net-next 00/11] Create common DPLL configuration API
@ 2023-07-21 15:46       ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:46 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 04:42:46PM CEST, vadim.fedorenko@linux.dev wrote:
>On 21.07.2023 12:14, Jiri Pirko wrote:
>> There are couple of issues that came up during our internal ci run:
>> 
>> 10:16:04  error: drivers/dpll/dpll_netlink.c:452:5: error: no previous prototype for '__dpll_device_change_ntf' [-Werror=missing-prototypes]
>> 10:16:04  error: drivers/dpll/dpll_netlink.c:1283:13: error: no previous prototype for 'dpll_netlink_fini' [-Werror=missing-prototypes]
>> 10:16:04  error: drivers/dpll/dpll_core.c:221:1: error: no previous prototype for 'dpll_xa_ref_dpll_find' [-Werror=missing-prototypes]
>> 
>> 10:27:31  error: drivers/dpll/dpll_core.c:220:21: warning: symbol 'dpll_xa_ref_dpll_find' was not declared. Should it be static?
>> 10:27:31  error: drivers/dpll/dpll_netlink.c:452:5: warning: symbol '__dpll_device_change_ntf' was not declared. Should it be static?
>> 10:27:31  error: drivers/dpll/dpll_netlink.c:1283:13: warning: symbol 'dpll_netlink_fini' was not declared. Should it be static?
>> 10:27:41  error: drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: a label can only be part of a statement and a declaration is not a statement
>> 
>> I believe that you didn't run make with C=2, otherwise you would hit
>> these.
>
>Yeah, I'll re-run the set patch-by-patch with C=2 next time.
>
>> 
>> Checkpatch issue:
>> 10:29:30  CHECK: struct mutex definition without comment
>> 10:29:30  #6581: FILE: drivers/net/ethernet/intel/ice/ice_dpll.h:85:
>> 10:29:30  +	struct mutex lock;
>
>Arkadiusz will take care of "ice" part.
>
>
>> Spelling errors:
>> 10:45:08  error: Documentation/netlink/specs/dpll.yaml:165: prority ==> priority
>> 10:45:08  error: include/uapi/linux/dpll.h:128: prority ==> priority
>> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.c:2008: userpsace ==> userspace
>> 10:45:08  error: drivers/net/ethernet/intel/ice/ice_dpll.h:20: properities ==> properties
>> 
>
>Will fix it.
>
>> 
>> Thu, Jul 20, 2023 at 11:18:52AM CEST, vadim.fedorenko@linux.dev 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
>> 
>> s/API aim/API aims/
>> 
>> 
>> > make it flexible and easy to cover special configurations.
>> 
>> I don't follow. How this could "aim to extend current pin configuration" ?
>> This is a new thing. Could you re-phrase?
>
>Not really new. PTP devices have already simple pin configurations, mlx5 is
>using it for some cards with external pins. The problem is that PTP subsystem
>covers only simple configuration of the pin and doesn't cover DPLL part at all.

Okay, please put the info in.


>
>> 
>> What's "special configuration"? Sounds odd.
>> 
>
>Yeah, "complex configurations" sounds better, will change it.
>
>> 
>> > 
>> > Netlink interface is based on ynl spec, it allows use of in-kernel
>> > tools/net/ynl/cli.py application to control the interface with properly
>> > formated command and json attribute strings. Here are few command
>> > examples of how it works with `ice` driver on supported NIC:
>> > 
>> > - dump dpll devices
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> 
>> "$#" looks a bit odd. Just "$" with "sudo" when you want to emphasize
>> root is needed to perform the command.
>> 
>> 
>> > --dump device-get
>> > [{'clock-id': 282574471561216,
>> >   'id': 0,
>> >   'lock-status': 'unlocked',
>> >   'mode': 'automatic',
>> >   'module-name': 'ice',
>> >   'type': 'eec'},
>> > {'clock-id': 282574471561216,
>> >   'id': 1,
>> >   'lock-status': 'unlocked',
>> >   'mode': 'automatic',
>> >   'module-name': 'ice',
>> >   'type': 'pps'}]
>> > 
>> > - get single pin info:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-get --json '{"pin-id":2}'
>> > {'clock-id': 282574471561216,
>> > 'module-name': 'ice',
>> > 'pin-board-label': 'C827_0-RCLKA',
>> > 'pin-dpll-caps': 6,
>> > 'pin-frequency': 1953125,
>> > 'pin-id': 2,
>> > 'pin-parenti-device': [{'id': 0,
>> 
>> This looks like manual edit went wrong :)
>> s/parenti/parent/
>> 
>
>Ahhh... yeah :)
>
>> 
>> >                          'pin-direction': 'input',
>> >                          'pin-prio': 11,
>> >                          'pin-state': 'selectable'},
>> >                         {'id': 1,
>> >                          'pin-direction': 'input',
>> >                          'pin-prio': 9,
>> >                          'pin-state': 'selectable'}],
>> > 'pin-type': 'mux'}
>> > 
>> > - set pin's state on dpll:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-state":2}}'
>> > 
>> > - set pin's prio on dpll:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":2, "pin-parent-device":{"id":1, "pin-prio":4}}'
>> > 
>> > - set pin's state on parent pin:
>> > $# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
>> > --do pin-set --json '{"pin-id":13, \
>> >                       "pin-parent-pin":{"pin-id":2, "pin-state":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] 109+ messages in thread

* Re: [PATCH 10/11] ptp_ocp: implement DPLL ops
  2023-07-20  9:19   ` Vadim Fedorenko
@ 2023-07-21 15:51     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:51 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:02AM CEST, vadim.fedorenko@linux.dev 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>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

Signed-off-by: Jiri Pirko <jiri@nvidia.com>

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

* Re: [PATCH 10/11] ptp_ocp: implement DPLL ops
@ 2023-07-21 15:51     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-21 15:51 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:19:02AM CEST, vadim.fedorenko@linux.dev 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>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

Signed-off-by: Jiri Pirko <jiri@nvidia.com>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 15:45                 ` Jiri Pirko
@ 2023-07-21 19:48                   ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 19:48 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 5:46 PM
>
>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 2:02 PM
>>>
>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>
>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>
>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>wrote:
>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>
>>>>>>>[...]
>>>>>>>
>>>>>>>
>>>>>>>>+/**
>>>>>>>>+ * 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
>>>>>>>>+ * @extack: error reporting
>>>>>>>>+ *
>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>+ *
>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>+ * Return:
>>>>>>>>+ * * 0 - OK
>>>>>>>>+ * * negative - error
>>>>>>>>+ */
>>>>>>>>+static int
>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>+{
>>>>>>>>+	u8 flags = 0;
>>>>>>>>+	int ret;
>>>>>>>>+
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>
>>>>>>
>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>to the state of the pin, but it is the other way around.
>>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>monitor
>>>>>>any of the pins.
>>>>>>
>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>diffrerent between:
>>>>>>>1) freerun mode
>>>>>>>2) automatic mode & all pins disabled?
>>>>>>
>>>>>>The difference:
>>>>>>Case I:
>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>AUTOMATIC
>>>>>>2. switch to AUTOMATIC
>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>
>>>>>>Case II:
>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>2. switch one valid source to SELECTABLE
>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>
>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>they
>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>monitoring,
>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>longer as
>>>>>>dpll need to start the process from scratch.
>>>>>
>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>least for the initial patchset version. If you really need it later on
>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>
>>>>
>>>>And we will have the same discussion later.. But implementation is already
>>>>there.
>>>
>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>merged, so anything which is blocking us and is totally optional (as
>>>this freerun mode) is better to be dropped.
>>>
>>
>>It is not blocking anything. Most of it was defined and available for
>>long time already. Only ice implementing set_mode is a new part.
>>No clue what is the problem you are implying here.
>
>Problem is that I believe you freerun mode should not exist. I believe
>it is wrong.
>
>
>>
>>>
>>>>As said in our previous discussion, without mode_set there is no point to
>>>>have
>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>the
>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>
>>>I don't see any problem in having enum value reserved. But it does not
>>>need to be there at all. You can add it to the end of the list when
>>>needed. No problem. This is not an argument.
>>>
>>
>>The argument is that I already implemented and tested, and have the need
>>for the
>>existence to set_mode to configure DPLL, which is there to switch the mode
>>between AUTOMATIC and FREERUN.
>>
>>>
>>>>
>>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>the
>>>>input and dpll's output won't be available.
>>>>
>>>>For the user there is a difference..
>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>measured.
>>>
>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>
>>>Please don't mix config and state. I think we untangled this in the past
>>>:/
>>
>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>
>You do. You want to force-change the state yet you mangle the mode in.
>The fact that some specific dpll implemented it as mode does not mean it
>has to be exposed like that to user. We have to find the right
>abstraction.
>

Just to make it clear:

AUTOMATIC:
- inputs monitored, validated, phase measurements available
- possible states: unlocked, locked, locked-ho-acq, holdover

FREERUN:
- inputs not monitored, not validated, no phase measurements available
- possible states: unlocked

>
>>
>>>
>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>to hit this button.
>>>
>>
>>As already said there are measurement in place in AUTOMATIC, there are no
>>such
>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>which
>>is a side effect of going to FREERUN.
>>
>>>
>>>
>>>>So probably most important fact that you are missing here: assuming the
>>>>user
>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>UNLOCKED
>>>>state but into HOLDOVER.
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>needs to be documented, please.
>>>>>>>
>>>>>>
>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>
>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>it in the first place in the last patchset version.
>>>>>
>>>>
>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>driver
>>>>as it should.
>>>
>>>I don't understand the fixation on a callback to be implemented. Just
>>>remove it. It can be easily added when needed. No problem.
>>>
>>
>>Well, I don't understand the fixation about removing it.
>
>It is needed only for your freerun mode, which is questionable. This
>discussion it not about mode_set. I don't care about it, if it is
>needed, should be there, if not, so be it.
>
>As you say, you need existance of your freerun mode to justify existence
>of mode_set(). Could you please, please drop both for now so we can
>move on? I'm tired of this. Thanks!
>

Reason for dpll subsystem is to control the dpll. So the mode_set and
different modes are there for the same reason.
Explained this multiple times already, we need a way to let the user switch
to FREERUN, so all the activities on dpll are stopped.

>
>>set_mode was there for a long time, now the callback is properly implemented
>>and you are trying to imply that this is not needed.
>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>to do its work.
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>connected with a single DPLL pin:
>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>
>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>
>>>>>>>Could you please describe following 2 flows?
>>>>>>>
>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>
>>>>>>>
>>>>>>>For mlx5 it goes like:
>>>>>>>
>>>>>>>DPLL device mode is MANUAL.
>>>>>>>1)
>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>    -> pin_id: 10
>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>    -> device_id: 2
>>>>>>
>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>relate to the dpll interface..
>>>>>
>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>
>>>>
>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>know already something about the dpll it is managing.
>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>daemon
>>>>learns it.
>>>
>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>
>>>
>>>>But let's park it, as this is not really relevant.
>>>
>>>Agreed.
>>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>CONNECTED
>>>>>>>
>>>>>>>2)
>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>    -> pin_id: 11
>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>    -> device_id: 2
>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>CONNECTED
>>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>
>>>>>>
>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>>the MUX pin).
>>>>>>
>>>>>>1)
>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>pin_id: 13
>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>all the
>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>
>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>state on DPLL device.
>>>>>
>>>>>
>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>with
>>>>>>   parent pin (pin-id:2)
>>>>>
>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>a parent pin.
>>>>>
>>>>>
>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>just
>>>>>need a simple thing: "select this pin as a source" :/
>>>>>
>>>>>
>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>
>>>>
>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>fact
>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>AUTOMATIC mode.
>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>type
>>>>pin.
>>>
>>>Sure, but does user care how complicated things are inside? The syncE
>>>daemon just cares for: "select netdev x as a source". However it is done
>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>work with it.
>>>
>>
>>This is dpll subsystem not SyncE one.
>
>SyncE is very legit use case of the UAPI. I would say perhaps the most
>important.
>

But it is still a dpll subsystem.

Thank you!
Arkadiusz

>
>>
>>>Do we need a dpll library to do this magic?
>>>
>>
>>IMHO rather SyncE library :)
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>pin_id: 14
>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>all the
>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>with
>>>>>>   parent pin (pin-id:2)
>>>>>>
>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>due to
>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>
>>>>>>Thank you!
>>>>>>Arkadiusz
>>>>>>
>>>>>>>
>>>>>>>Thanks!
>>>>>>>
>>>>>>>
>>>>>>>[...]
>>>>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-21 19:48                   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-21 19:48 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 5:46 PM
>
>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 2:02 PM
>>>
>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>
>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>
>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>wrote:
>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>
>>>>>>>[...]
>>>>>>>
>>>>>>>
>>>>>>>>+/**
>>>>>>>>+ * 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
>>>>>>>>+ * @extack: error reporting
>>>>>>>>+ *
>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>+ *
>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>+ * Return:
>>>>>>>>+ * * 0 - OK
>>>>>>>>+ * * negative - error
>>>>>>>>+ */
>>>>>>>>+static int
>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>+{
>>>>>>>>+	u8 flags = 0;
>>>>>>>>+	int ret;
>>>>>>>>+
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>
>>>>>>
>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>to the state of the pin, but it is the other way around.
>>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>monitor
>>>>>>any of the pins.
>>>>>>
>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>diffrerent between:
>>>>>>>1) freerun mode
>>>>>>>2) automatic mode & all pins disabled?
>>>>>>
>>>>>>The difference:
>>>>>>Case I:
>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>AUTOMATIC
>>>>>>2. switch to AUTOMATIC
>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>
>>>>>>Case II:
>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>2. switch one valid source to SELECTABLE
>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>
>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>they
>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>monitoring,
>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>longer as
>>>>>>dpll need to start the process from scratch.
>>>>>
>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>least for the initial patchset version. If you really need it later on
>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>
>>>>
>>>>And we will have the same discussion later.. But implementation is already
>>>>there.
>>>
>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>merged, so anything which is blocking us and is totally optional (as
>>>this freerun mode) is better to be dropped.
>>>
>>
>>It is not blocking anything. Most of it was defined and available for
>>long time already. Only ice implementing set_mode is a new part.
>>No clue what is the problem you are implying here.
>
>Problem is that I believe you freerun mode should not exist. I believe
>it is wrong.
>
>
>>
>>>
>>>>As said in our previous discussion, without mode_set there is no point to
>>>>have
>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>the
>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>
>>>I don't see any problem in having enum value reserved. But it does not
>>>need to be there at all. You can add it to the end of the list when
>>>needed. No problem. This is not an argument.
>>>
>>
>>The argument is that I already implemented and tested, and have the need
>>for the
>>existence to set_mode to configure DPLL, which is there to switch the mode
>>between AUTOMATIC and FREERUN.
>>
>>>
>>>>
>>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>the
>>>>input and dpll's output won't be available.
>>>>
>>>>For the user there is a difference..
>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>measured.
>>>
>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>
>>>Please don't mix config and state. I think we untangled this in the past
>>>:/
>>
>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>
>You do. You want to force-change the state yet you mangle the mode in.
>The fact that some specific dpll implemented it as mode does not mean it
>has to be exposed like that to user. We have to find the right
>abstraction.
>

Just to make it clear:

AUTOMATIC:
- inputs monitored, validated, phase measurements available
- possible states: unlocked, locked, locked-ho-acq, holdover

FREERUN:
- inputs not monitored, not validated, no phase measurements available
- possible states: unlocked

>
>>
>>>
>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>to hit this button.
>>>
>>
>>As already said there are measurement in place in AUTOMATIC, there are no
>>such
>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>which
>>is a side effect of going to FREERUN.
>>
>>>
>>>
>>>>So probably most important fact that you are missing here: assuming the
>>>>user
>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>UNLOCKED
>>>>state but into HOLDOVER.
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>needs to be documented, please.
>>>>>>>
>>>>>>
>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>
>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>it in the first place in the last patchset version.
>>>>>
>>>>
>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>driver
>>>>as it should.
>>>
>>>I don't understand the fixation on a callback to be implemented. Just
>>>remove it. It can be easily added when needed. No problem.
>>>
>>
>>Well, I don't understand the fixation about removing it.
>
>It is needed only for your freerun mode, which is questionable. This
>discussion it not about mode_set. I don't care about it, if it is
>needed, should be there, if not, so be it.
>
>As you say, you need existance of your freerun mode to justify existence
>of mode_set(). Could you please, please drop both for now so we can
>move on? I'm tired of this. Thanks!
>

Reason for dpll subsystem is to control the dpll. So the mode_set and
different modes are there for the same reason.
Explained this multiple times already, we need a way to let the user switch
to FREERUN, so all the activities on dpll are stopped.

>
>>set_mode was there for a long time, now the callback is properly implemented
>>and you are trying to imply that this is not needed.
>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>to do its work.
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>connected with a single DPLL pin:
>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>
>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>
>>>>>>>Could you please describe following 2 flows?
>>>>>>>
>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>
>>>>>>>
>>>>>>>For mlx5 it goes like:
>>>>>>>
>>>>>>>DPLL device mode is MANUAL.
>>>>>>>1)
>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>    -> pin_id: 10
>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>    -> device_id: 2
>>>>>>
>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>relate to the dpll interface..
>>>>>
>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>
>>>>
>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>know already something about the dpll it is managing.
>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>daemon
>>>>learns it.
>>>
>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>
>>>
>>>>But let's park it, as this is not really relevant.
>>>
>>>Agreed.
>>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>CONNECTED
>>>>>>>
>>>>>>>2)
>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>    -> pin_id: 11
>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>    -> device_id: 2
>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>CONNECTED
>>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>
>>>>>>
>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>>the MUX pin).
>>>>>>
>>>>>>1)
>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>pin_id: 13
>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>all the
>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>
>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>state on DPLL device.
>>>>>
>>>>>
>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>with
>>>>>>   parent pin (pin-id:2)
>>>>>
>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>a parent pin.
>>>>>
>>>>>
>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>just
>>>>>need a simple thing: "select this pin as a source" :/
>>>>>
>>>>>
>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>
>>>>
>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>fact
>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>AUTOMATIC mode.
>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>type
>>>>pin.
>>>
>>>Sure, but does user care how complicated things are inside? The syncE
>>>daemon just cares for: "select netdev x as a source". However it is done
>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>work with it.
>>>
>>
>>This is dpll subsystem not SyncE one.
>
>SyncE is very legit use case of the UAPI. I would say perhaps the most
>important.
>

But it is still a dpll subsystem.

Thank you!
Arkadiusz

>
>>
>>>Do we need a dpll library to do this magic?
>>>
>>
>>IMHO rather SyncE library :)
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>pin_id: 14
>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>all the
>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>with
>>>>>>   parent pin (pin-id:2)
>>>>>>
>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>due to
>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>
>>>>>>Thank you!
>>>>>>Arkadiusz
>>>>>>
>>>>>>>
>>>>>>>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] 109+ messages in thread

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21  7:33         ` Jiri Pirko
@ 2023-07-22  2:08           ` Jakub Kicinski
  -1 siblings, 0 replies; 109+ messages in thread
From: Jakub Kicinski @ 2023-07-22  2:08 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Fri, 21 Jul 2023 09:33:14 +0200 Jiri Pirko wrote:
> >d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
> >   parent pin (pin-id:2)  
> 
> For this you need pin_id and pin_parent_id because you set the state on
> a parent pin.
> 
> 
> Yeah, this is exactly why I initially was in favour of hiding all the
> muxes and magic around it hidden from the user. Now every userspace app
> working with this has to implement a logic of tracking pin and the mux
> parents (possibly multiple levels) and configure everything. But it just
> need a simple thing: "select this pin as a source" :/
> 
> 
> Jakub, isn't this sort of unnecessary HW-details complexicity exposure
> in UAPI you were against in the past? Am I missing something?

From just reading what I'm quoting - I don't think so.
Muxes are meaningful because they limit valid configurations.
We can implement "automatic mutex config" in the kernel
if user wants it, centrally in the core, otherwise each
driver will have to do it on its own.

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-22  2:08           ` Jakub Kicinski
  0 siblings, 0 replies; 109+ messages in thread
From: Jakub Kicinski @ 2023-07-22  2:08 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Fri, 21 Jul 2023 09:33:14 +0200 Jiri Pirko wrote:
> >d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED with
> >   parent pin (pin-id:2)  
> 
> For this you need pin_id and pin_parent_id because you set the state on
> a parent pin.
> 
> 
> Yeah, this is exactly why I initially was in favour of hiding all the
> muxes and magic around it hidden from the user. Now every userspace app
> working with this has to implement a logic of tracking pin and the mux
> parents (possibly multiple levels) and configure everything. But it just
> need a simple thing: "select this pin as a source" :/
> 
> 
> Jakub, isn't this sort of unnecessary HW-details complexicity exposure
> in UAPI you were against in the past? Am I missing something?

From just reading what I'm quoting - I don't think so.
Muxes are meaningful because they limit valid configurations.
We can implement "automatic mutex config" in the kernel
if user wants it, centrally in the core, otherwise each
driver will have to do it on its own.

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 19:48                   ` Kubalewski, Arkadiusz
@ 2023-07-22  6:37                     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-22  6:37 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 5:46 PM
>>
>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>
>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>
>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>wrote:
>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>
>>>>>>>>[...]
>>>>>>>>
>>>>>>>>
>>>>>>>>>+/**
>>>>>>>>>+ * 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
>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>+ *
>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>>+ *
>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>+ * Return:
>>>>>>>>>+ * * 0 - OK
>>>>>>>>>+ * * negative - error
>>>>>>>>>+ */
>>>>>>>>>+static int
>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>+{
>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>+	int ret;
>>>>>>>>>+
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>
>>>>>>>
>>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>monitor
>>>>>>>any of the pins.
>>>>>>>
>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>>diffrerent between:
>>>>>>>>1) freerun mode
>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>
>>>>>>>The difference:
>>>>>>>Case I:
>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>AUTOMATIC
>>>>>>>2. switch to AUTOMATIC
>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>
>>>>>>>Case II:
>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>
>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>they
>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>monitoring,
>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>longer as
>>>>>>>dpll need to start the process from scratch.
>>>>>>
>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>
>>>>>
>>>>>And we will have the same discussion later.. But implementation is already
>>>>>there.
>>>>
>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>merged, so anything which is blocking us and is totally optional (as
>>>>this freerun mode) is better to be dropped.
>>>>
>>>
>>>It is not blocking anything. Most of it was defined and available for
>>>long time already. Only ice implementing set_mode is a new part.
>>>No clue what is the problem you are implying here.
>>
>>Problem is that I believe you freerun mode should not exist. I believe
>>it is wrong.
>>
>>
>>>
>>>>
>>>>>As said in our previous discussion, without mode_set there is no point to
>>>>>have
>>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>>the
>>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>>
>>>>I don't see any problem in having enum value reserved. But it does not
>>>>need to be there at all. You can add it to the end of the list when
>>>>needed. No problem. This is not an argument.
>>>>
>>>
>>>The argument is that I already implemented and tested, and have the need
>>>for the
>>>existence to set_mode to configure DPLL, which is there to switch the mode
>>>between AUTOMATIC and FREERUN.
>>>
>>>>
>>>>>
>>>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>>the
>>>>>input and dpll's output won't be available.
>>>>>
>>>>>For the user there is a difference..
>>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>>measured.
>>>>
>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>
>>>>Please don't mix config and state. I think we untangled this in the past
>>>>:/
>>>
>>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>>
>>You do. You want to force-change the state yet you mangle the mode in.
>>The fact that some specific dpll implemented it as mode does not mean it
>>has to be exposed like that to user. We have to find the right
>>abstraction.
>>
>
>Just to make it clear:
>
>AUTOMATIC:
>- inputs monitored, validated, phase measurements available
>- possible states: unlocked, locked, locked-ho-acq, holdover
>
>FREERUN:
>- inputs not monitored, not validated, no phase measurements available
>- possible states: unlocked

This is your implementation of DPLL. Others may have it done
differently. But the fact the input is monitored or not, does not make
any difference from user perspective.

When he has automatic mode and does:
1) disconnect all pins
2) reset state    (however you implement it in the driver is totaly up
		   to the device, you may go to your freerun dpll mode
		   internally and to automatic back, up to you)
 -> state will go to unlocked

The behaviour is exactly the same, without any special mode.

We are talking about UAPI here. It should provide the abstraction, leaving the
internal implementation behind the curtain. What is important is:
1) clear configuration knobs
2) the outcome (hw behaviour)



>
>>
>>>
>>>>
>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>to hit this button.
>>>>
>>>
>>>As already said there are measurement in place in AUTOMATIC, there are no
>>>such
>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>which
>>>is a side effect of going to FREERUN.
>>>
>>>>
>>>>
>>>>>So probably most important fact that you are missing here: assuming the
>>>>>user
>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>UNLOCKED
>>>>>state but into HOLDOVER.
>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>>needs to be documented, please.
>>>>>>>>
>>>>>>>
>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>
>>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>>it in the first place in the last patchset version.
>>>>>>
>>>>>
>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>driver
>>>>>as it should.
>>>>
>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>remove it. It can be easily added when needed. No problem.
>>>>
>>>
>>>Well, I don't understand the fixation about removing it.
>>
>>It is needed only for your freerun mode, which is questionable. This
>>discussion it not about mode_set. I don't care about it, if it is
>>needed, should be there, if not, so be it.
>>
>>As you say, you need existance of your freerun mode to justify existence
>>of mode_set(). Could you please, please drop both for now so we can
>>move on? I'm tired of this. Thanks!
>>
>
>Reason for dpll subsystem is to control the dpll. So the mode_set and
>different modes are there for the same reason.
>Explained this multiple times already, we need a way to let the user switch
>to FREERUN, so all the activities on dpll are stopped.
>
>>
>>>set_mode was there for a long time, now the callback is properly implemented
>>>and you are trying to imply that this is not needed.
>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>to do its work.
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>connected with a single DPLL pin:
>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>
>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>
>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>
>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>
>>>>>>>>
>>>>>>>>For mlx5 it goes like:
>>>>>>>>
>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>1)
>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>    -> pin_id: 10
>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>    -> device_id: 2
>>>>>>>
>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>>relate to the dpll interface..
>>>>>>
>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>
>>>>>
>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>>know already something about the dpll it is managing.
>>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>>daemon
>>>>>learns it.
>>>>
>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>
>>>>
>>>>>But let's park it, as this is not really relevant.
>>>>
>>>>Agreed.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>CONNECTED
>>>>>>>>
>>>>>>>>2)
>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>    -> pin_id: 11
>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>>    -> device_id: 2
>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>CONNECTED
>>>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>
>>>>>>>
>>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>>>the MUX pin).
>>>>>>>
>>>>>>>1)
>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>pin_id: 13
>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>all the
>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>
>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>>state on DPLL device.
>>>>>>
>>>>>>
>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>>with
>>>>>>>   parent pin (pin-id:2)
>>>>>>
>>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>>a parent pin.
>>>>>>
>>>>>>
>>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>just
>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>
>>>>>>
>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>
>>>>>
>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>fact
>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>AUTOMATIC mode.
>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>type
>>>>>pin.
>>>>
>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>work with it.
>>>>
>>>
>>>This is dpll subsystem not SyncE one.
>>
>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>important.
>>
>
>But it is still a dpll subsystem.
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>>Do we need a dpll library to do this magic?
>>>>
>>>
>>>IMHO rather SyncE library :)
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>pin_id: 14
>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>all the
>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>>with
>>>>>>>   parent pin (pin-id:2)
>>>>>>>
>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>>due to
>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>
>>>>>>>Thank you!
>>>>>>>Arkadiusz
>>>>>>>
>>>>>>>>
>>>>>>>>Thanks!
>>>>>>>>
>>>>>>>>
>>>>>>>>[...]
>>>>>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-22  6:37                     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-22  6:37 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 5:46 PM
>>
>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>
>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>
>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>wrote:
>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>
>>>>>>>>[...]
>>>>>>>>
>>>>>>>>
>>>>>>>>>+/**
>>>>>>>>>+ * 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
>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>+ *
>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>>+ *
>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>+ * Return:
>>>>>>>>>+ * * 0 - OK
>>>>>>>>>+ * * negative - error
>>>>>>>>>+ */
>>>>>>>>>+static int
>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>+{
>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>+	int ret;
>>>>>>>>>+
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>
>>>>>>>
>>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>The dpll device mode is a state of DPLL before pins are even considered.
>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>monitor
>>>>>>>any of the pins.
>>>>>>>
>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>>diffrerent between:
>>>>>>>>1) freerun mode
>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>
>>>>>>>The difference:
>>>>>>>Case I:
>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>AUTOMATIC
>>>>>>>2. switch to AUTOMATIC
>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>
>>>>>>>Case II:
>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>
>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>they
>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>monitoring,
>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>longer as
>>>>>>>dpll need to start the process from scratch.
>>>>>>
>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>
>>>>>
>>>>>And we will have the same discussion later.. But implementation is already
>>>>>there.
>>>>
>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>merged, so anything which is blocking us and is totally optional (as
>>>>this freerun mode) is better to be dropped.
>>>>
>>>
>>>It is not blocking anything. Most of it was defined and available for
>>>long time already. Only ice implementing set_mode is a new part.
>>>No clue what is the problem you are implying here.
>>
>>Problem is that I believe you freerun mode should not exist. I believe
>>it is wrong.
>>
>>
>>>
>>>>
>>>>>As said in our previous discussion, without mode_set there is no point to
>>>>>have
>>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>>the
>>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>>
>>>>I don't see any problem in having enum value reserved. But it does not
>>>>need to be there at all. You can add it to the end of the list when
>>>>needed. No problem. This is not an argument.
>>>>
>>>
>>>The argument is that I already implemented and tested, and have the need
>>>for the
>>>existence to set_mode to configure DPLL, which is there to switch the mode
>>>between AUTOMATIC and FREERUN.
>>>
>>>>
>>>>>
>>>>>Also this is not HW implementation detail but a synchronizer chip feature,
>>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>>the
>>>>>input and dpll's output won't be available.
>>>>>
>>>>>For the user there is a difference..
>>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>>measured.
>>>>
>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>
>>>>Please don't mix config and state. I think we untangled this in the past
>>>>:/
>>>
>>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>>
>>You do. You want to force-change the state yet you mangle the mode in.
>>The fact that some specific dpll implemented it as mode does not mean it
>>has to be exposed like that to user. We have to find the right
>>abstraction.
>>
>
>Just to make it clear:
>
>AUTOMATIC:
>- inputs monitored, validated, phase measurements available
>- possible states: unlocked, locked, locked-ho-acq, holdover
>
>FREERUN:
>- inputs not monitored, not validated, no phase measurements available
>- possible states: unlocked

This is your implementation of DPLL. Others may have it done
differently. But the fact the input is monitored or not, does not make
any difference from user perspective.

When he has automatic mode and does:
1) disconnect all pins
2) reset state    (however you implement it in the driver is totaly up
		   to the device, you may go to your freerun dpll mode
		   internally and to automatic back, up to you)
 -> state will go to unlocked

The behaviour is exactly the same, without any special mode.

We are talking about UAPI here. It should provide the abstraction, leaving the
internal implementation behind the curtain. What is important is:
1) clear configuration knobs
2) the outcome (hw behaviour)



>
>>
>>>
>>>>
>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>to hit this button.
>>>>
>>>
>>>As already said there are measurement in place in AUTOMATIC, there are no
>>>such
>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>which
>>>is a side effect of going to FREERUN.
>>>
>>>>
>>>>
>>>>>So probably most important fact that you are missing here: assuming the
>>>>>user
>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>UNLOCKED
>>>>>state but into HOLDOVER.
>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>>needs to be documented, please.
>>>>>>>>
>>>>>>>
>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>
>>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>>it in the first place in the last patchset version.
>>>>>>
>>>>>
>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>driver
>>>>>as it should.
>>>>
>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>remove it. It can be easily added when needed. No problem.
>>>>
>>>
>>>Well, I don't understand the fixation about removing it.
>>
>>It is needed only for your freerun mode, which is questionable. This
>>discussion it not about mode_set. I don't care about it, if it is
>>needed, should be there, if not, so be it.
>>
>>As you say, you need existance of your freerun mode to justify existence
>>of mode_set(). Could you please, please drop both for now so we can
>>move on? I'm tired of this. Thanks!
>>
>
>Reason for dpll subsystem is to control the dpll. So the mode_set and
>different modes are there for the same reason.
>Explained this multiple times already, we need a way to let the user switch
>to FREERUN, so all the activities on dpll are stopped.
>
>>
>>>set_mode was there for a long time, now the callback is properly implemented
>>>and you are trying to imply that this is not needed.
>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>to do its work.
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>connected with a single DPLL pin:
>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>
>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>
>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>
>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>
>>>>>>>>
>>>>>>>>For mlx5 it goes like:
>>>>>>>>
>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>1)
>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>    -> pin_id: 10
>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>    -> device_id: 2
>>>>>>>
>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>>relate to the dpll interface..
>>>>>>
>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>
>>>>>
>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>>know already something about the dpll it is managing.
>>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>>daemon
>>>>>learns it.
>>>>
>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>
>>>>
>>>>>But let's park it, as this is not really relevant.
>>>>
>>>>Agreed.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>CONNECTED
>>>>>>>>
>>>>>>>>2)
>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>    -> pin_id: 11
>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>>    -> device_id: 2
>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>CONNECTED
>>>>>>>> (that will in HW disconnect previously connected pin 10, there will be
>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>
>>>>>>>
>>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>>clock pins which are not directly connected to a dpll (connected through
>>>>>>>the MUX pin).
>>>>>>>
>>>>>>>1)
>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>pin_id: 13
>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>all the
>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>
>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>>state on DPLL device.
>>>>>>
>>>>>>
>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>>with
>>>>>>>   parent pin (pin-id:2)
>>>>>>
>>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>>a parent pin.
>>>>>>
>>>>>>
>>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>just
>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>
>>>>>>
>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>
>>>>>
>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>fact
>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>AUTOMATIC mode.
>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>type
>>>>>pin.
>>>>
>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>work with it.
>>>>
>>>
>>>This is dpll subsystem not SyncE one.
>>
>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>important.
>>
>
>But it is still a dpll subsystem.
>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>>Do we need a dpll library to do this magic?
>>>>
>>>
>>>IMHO rather SyncE library :)
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>pin_id: 14
>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>all the
>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>>with
>>>>>>>   parent pin (pin-id:2)
>>>>>>>
>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>>due to
>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>
>>>>>>>Thank you!
>>>>>>>Arkadiusz
>>>>>>>
>>>>>>>>
>>>>>>>>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] 109+ messages in thread

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-22  6:37                     ` Jiri Pirko
  (?)
@ 2023-07-24 15:03                     ` Kubalewski, Arkadiusz
  2023-07-25  8:03                       ` Jiri Pirko
  -1 siblings, 1 reply; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-24 15:03 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Saturday, July 22, 2023 8:37 AM
>
>Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 5:46 PM
>>>
>>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>>
>>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>>
>>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>wrote:
>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>>
>>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>>wrote:
>>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>>
>>>>>>>>>[...]
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>+/**
>>>>>>>>>>+ * 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
>>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>>+ *
>>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>>>+ *
>>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>>+ * Return:
>>>>>>>>>>+ * * 0 - OK
>>>>>>>>>>+ * * negative - error
>>>>>>>>>>+ */
>>>>>>>>>>+static int
>>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>>+{
>>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>>+	int ret;
>>>>>>>>>>+
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>>
>>>>>>>>
>>>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>>The dpll device mode is a state of DPLL before pins are even
>>>>>>>>considered.
>>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>>monitor
>>>>>>>>any of the pins.
>>>>>>>>
>>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>>>diffrerent between:
>>>>>>>>>1) freerun mode
>>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>>
>>>>>>>>The difference:
>>>>>>>>Case I:
>>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>>AUTOMATIC
>>>>>>>>2. switch to AUTOMATIC
>>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>>
>>>>>>>>Case II:
>>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>>
>>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>>they
>>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>>monitoring,
>>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>>longer as
>>>>>>>>dpll need to start the process from scratch.
>>>>>>>
>>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>>
>>>>>>
>>>>>>And we will have the same discussion later.. But implementation is
>>>>>>already
>>>>>>there.
>>>>>
>>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>>merged, so anything which is blocking us and is totally optional (as
>>>>>this freerun mode) is better to be dropped.
>>>>>
>>>>
>>>>It is not blocking anything. Most of it was defined and available for
>>>>long time already. Only ice implementing set_mode is a new part.
>>>>No clue what is the problem you are implying here.
>>>
>>>Problem is that I believe you freerun mode should not exist. I believe
>>>it is wrong.
>>>
>>>
>>>>
>>>>>
>>>>>>As said in our previous discussion, without mode_set there is no point to
>>>>>>have
>>>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>>>the
>>>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>>>
>>>>>I don't see any problem in having enum value reserved. But it does not
>>>>>need to be there at all. You can add it to the end of the list when
>>>>>needed. No problem. This is not an argument.
>>>>>
>>>>
>>>>The argument is that I already implemented and tested, and have the need
>>>>for the
>>>>existence to set_mode to configure DPLL, which is there to switch the mode
>>>>between AUTOMATIC and FREERUN.
>>>>
>>>>>
>>>>>>
>>>>>>Also this is not HW implementation detail but a synchronizer chip
>>>>>>feature,
>>>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>>>the
>>>>>>input and dpll's output won't be available.
>>>>>>
>>>>>>For the user there is a difference..
>>>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>>>measured.
>>>>>
>>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>>
>>>>>Please don't mix config and state. I think we untangled this in the past
>>>>>:/
>>>>
>>>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>>>
>>>You do. You want to force-change the state yet you mangle the mode in.
>>>The fact that some specific dpll implemented it as mode does not mean it
>>>has to be exposed like that to user. We have to find the right
>>>abstraction.
>>>
>>
>>Just to make it clear:
>>
>>AUTOMATIC:
>>- inputs monitored, validated, phase measurements available
>>- possible states: unlocked, locked, locked-ho-acq, holdover
>>
>>FREERUN:
>>- inputs not monitored, not validated, no phase measurements available
>>- possible states: unlocked
>
>This is your implementation of DPLL. Others may have it done
>differently. But the fact the input is monitored or not, does not make
>any difference from user perspective.
>
>When he has automatic mode and does:
>1) disconnect all pins
>2) reset state    (however you implement it in the driver is totaly up
>		   to the device, you may go to your freerun dpll mode
>		   internally and to automatic back, up to you)
> -> state will go to unlocked
>
>The behaviour is exactly the same, without any special mode.

In this case there is special reset button, which doesn't exist in
reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
to pretend the some kind of reset has happened, where in reality dpll went to
FREERUN and AUTOMATIC.
For me it seems it seems like unnecessary complication of user's life.
The idea of FREERUN mode is to run dpll on its system clock, so all the
"external" dpll sources shall be disconnected when dpll is in FREERUN.
Let's assume your HW doesn't have a FREERUN, can't you just create it by
disconnecting all the sources? 
BTW, what chip are you using on mlx5 for this?
I don't understand why the user would have to mangle state of all the pins just
to stop dpll's work if he could just go into FREERUN and voila. Also what if
user doesn't want change the configuration of the pins at all, and he just want
to desynchronize it's dpll for i.e. testing reason.

>
>We are talking about UAPI here. It should provide the abstraction, leaving
>the
>internal implementation behind the curtain. What is important is:
>1) clear configuration knobs
>2) the outcome (hw behaviour)
>
>
>
>>
>>>
>>>>
>>>>>
>>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>>to hit this button.
>>>>>
>>>>
>>>>As already said there are measurement in place in AUTOMATIC, there are no
>>>>such
>>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>>which
>>>>is a side effect of going to FREERUN.
>>>>
>>>>>
>>>>>
>>>>>>So probably most important fact that you are missing here: assuming the
>>>>>>user
>>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>>UNLOCKED
>>>>>>state but into HOLDOVER.
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>>>needs to be documented, please.
>>>>>>>>>
>>>>>>>>
>>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>>
>>>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>>>it in the first place in the last patchset version.
>>>>>>>
>>>>>>
>>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>>driver
>>>>>>as it should.
>>>>>
>>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>>remove it. It can be easily added when needed. No problem.
>>>>>
>>>>
>>>>Well, I don't understand the fixation about removing it.
>>>
>>>It is needed only for your freerun mode, which is questionable. This
>>>discussion it not about mode_set. I don't care about it, if it is
>>>needed, should be there, if not, so be it.
>>>
>>>As you say, you need existance of your freerun mode to justify existence
>>>of mode_set(). Could you please, please drop both for now so we can
>>>move on? I'm tired of this. Thanks!
>>>
>>
>>Reason for dpll subsystem is to control the dpll. So the mode_set and
>>different modes are there for the same reason.
>>Explained this multiple times already, we need a way to let the user switch
>>to FREERUN, so all the activities on dpll are stopped.
>>
>>>
>>>>set_mode was there for a long time, now the callback is properly
>>>>implemented
>>>>and you are trying to imply that this is not needed.
>>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>>to do its work.
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>>connected with a single DPLL pin:
>>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>>
>>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>>
>>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>>
>>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>For mlx5 it goes like:
>>>>>>>>>
>>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>>1)
>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>>    -> pin_id: 10
>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>>    -> device_id: 2
>>>>>>>>
>>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>>>relate to the dpll interface..
>>>>>>>
>>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>>
>>>>>>
>>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>>>know already something about the dpll it is managing.
>>>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>>>daemon
>>>>>>learns it.
>>>>>
>>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>>
>>>>>
>>>>>>But let's park it, as this is not really relevant.
>>>>>
>>>>>Agreed.
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>>CONNECTED
>>>>>>>>>
>>>>>>>>>2)
>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>>    -> pin_id: 11
>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>>>    -> device_id: 2
>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>>CONNECTED
>>>>>>>>> (that will in HW disconnect previously connected pin 10, there
>>>>>>>>>will be
>>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>>
>>>>>>>>
>>>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>>>clock pins which are not directly connected to a dpll (connected
>>>>>>>>through
>>>>>>>>the MUX pin).
>>>>>>>>
>>>>>>>>1)
>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>pin_id: 13
>>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>>all the
>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>
>>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>>>state on DPLL device.
>>>>>>>
>>>>>>>
>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>>>with
>>>>>>>>   parent pin (pin-id:2)
>>>>>>>
>>>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>>>a parent pin.
>>>>>>>
>>>>>>>
>>>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>>just
>>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>>
>>>>>>>
>>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>>
>>>>>>
>>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>>fact
>>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>>AUTOMATIC mode.
>>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>>type
>>>>>>pin.
>>>>>
>>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>>work with it.
>>>>>
>>>>
>>>>This is dpll subsystem not SyncE one.
>>>
>>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>>important.
>>>
>>
>>But it is still a dpll subsystem.
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>>Do we need a dpll library to do this magic?
>>>>>
>>>>
>>>>IMHO rather SyncE library :)
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>>
>>>>>>Thank you!
>>>>>>Arkadiusz
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>pin_id: 14
>>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>>all the
>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>>>with
>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>
>>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>>>due to
>>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>>
>>>>>>>>Thank you!
>>>>>>>>Arkadiusz
>>>>>>>>
>>>>>>>>>
>>>>>>>>>Thanks!
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>[...]
>>>>>>

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

* Re: [PATCH net-next 04/11] dpll: spec: Add Netlink spec in YAML
  2023-07-20  9:18   ` Vadim Fedorenko
  (?)
@ 2023-07-24 15:52   ` Simon Horman
  -1 siblings, 0 replies; 109+ messages in thread
From: Simon Horman @ 2023-07-24 15:52 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Thu, Jul 20, 2023 at 10:18:56AM +0100, Vadim Fedorenko wrote:

...

Hi Vadim,

Some minor feedback from my side.

> diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml

...

> +  -
> +    type: flags
> +    name: pin-caps
> +    doc: |
> +      defines possible capabilities of a pin, valid flags on
> +      DPLL_A_PIN_CAPS attribute
> +    entries:
> +      -
> +        name: direction-can-change
> +        doc: pin direction can be changed
> +      -
> +        name: priority-can-change
> +        doc: pin prority can be changed

nit: prority -> priority

...

> diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h

...

> +/**
> + * enum dpll_mode - working modes a dpll can support, differentiates if and how
> + *   dpll selects one of its inputs to syntonize with it, valid values for
> + *   DPLL_A_MODE attribute
> + * @DPLL_MODE_MANUAL: input can be only selected by sending a request to dpll
> + * @DPLL_MODE_AUTOMATIC: highest prio input pin auto selected by dpll
> + * @DPLL_MODE_FREERUN: dpll driven on system clk
> + */
> +enum dpll_mode {
> +	DPLL_MODE_MANUAL = 1,
> +	DPLL_MODE_AUTOMATIC,
> +	DPLL_MODE_FREERUN,

As __DPLL_MODE_MAX and DPLL_MODE_MAX are (rightly) not included
in the kernel doc above. I think it is appropriate to add the following
here.

	/* private: */

Likewise in several other places in this patch.

...

> +/**
> + * enum dpll_pin_caps - defines possible capabilities of a pin, valid flags on
> + *   DPLL_A_PIN_CAPS attribute
> + * @DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE: pin direction can be changed
> + * @DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE: pin prority can be changed

prority -> priority

> + * @DPLL_PIN_CAPS_STATE_CAN_CHANGE: pin state can be changed
> + */

...

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

* Re: [PATCH net-next 05/11] dpll: core: Add DPLL framework base functions
  2023-07-20  9:18   ` Vadim Fedorenko
  (?)
@ 2023-07-24 15:56   ` Simon Horman
  -1 siblings, 0 replies; 109+ messages in thread
From: Simon Horman @ 2023-07-24 15:56 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Thu, Jul 20, 2023 at 10:18:57AM +0100, Vadim Fedorenko wrote:

...

> diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
> new file mode 100644
> index 000000000000..3574bebf2c63
> --- /dev/null
> +++ b/drivers/dpll/dpll_core.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
> + *  Copyright (c) 2023 Intel and affiliates
> + */
> +
> +#ifndef __DPLL_CORE_H__
> +#define __DPLL_CORE_H__
> +
> +#include <linux/dpll.h>
> +#include <linux/list.h>
> +#include <linux/refcount.h>
> +#include "dpll_netlink.h"

Hi Vadim,

dpll_netlink.h is used here.
But that file it isn't added until the following patch.

...

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

* Re: [PATCH net-next 06/11] dpll: netlink: Add DPLL framework base functions
  2023-07-20  9:18   ` Vadim Fedorenko
  (?)
@ 2023-07-24 16:34   ` Simon Horman
  -1 siblings, 0 replies; 109+ messages in thread
From: Simon Horman @ 2023-07-24 16:34 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Thu, Jul 20, 2023 at 10:18:58AM +0100, Vadim Fedorenko wrote:
> DPLL framework is used to represent and configure DPLL devices
> in systems. Each device that has DPLL and can configure inputs
> and outputs can use this framework.

Hi Vadim,

some minor feedback from my side.

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

subsytem -> subsystem

...

> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c

...

> +/**
> + * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
> + * @msg: pointer to sk_buff message to attach a pin handle
> + * @pin: pin pointer
> + *
> + * Return:
> + * * 0 - success
> + * * -EMSGSIZE - no space in message to attach pin handle
> + */
> +int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)

This function seems to only be used in this file.
Should it be static.

...

> +static int
> +dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
> +			       struct dpll_pin_ref *ref,
> +			       struct netlink_ext_ack *extack)
> +{
> +	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
> +	struct dpll_device *dpll = ref->dpll;
> +	enum dpll_pin_state state;
> +	int ret;
> +
> +

nit: one blank line is enough

...

> +static int
> +dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
> +		      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
> +{
> +	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
> +	struct dpll_device *dpll = ref->dpll;
> +	struct nlattr *nest;
> +	int fs, ret;
> +	u64 freq;
> +
> +	if (!ops->frequency_get)
> +		return 0;
> +	ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
> +				 dpll_priv(dpll), &freq, extack);
> +	if (ret)
> +		return ret;
> +	if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
> +		return -EMSGSIZE;
> +	for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
> +		nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
> +		if (!nest)
> +			return -EMSGSIZE;
> +		freq = pin->prop->freq_supported[fs].min;
> +		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
> +				   &freq, 0)) {

nit: The indention of the line above isn't quite right.
     There is one space too many.

> +			nla_nest_cancel(msg, nest);
> +			return -EMSGSIZE;
> +		}
> +		freq = pin->prop->freq_supported[fs].max;
> +		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
> +				   &freq, 0)) {

Ditto.

> +			nla_nest_cancel(msg, nest);
> +			return -EMSGSIZE;
> +		}
> +		nla_nest_end(msg, nest);
> +	}
> +
> +	return 0;
> +}

...

> +static int
> +dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
> +{
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int ret;
> +
> +	if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))
> +		return -ENODEV;
> +	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +	if (!msg)
> +		return -ENOMEM;
> +	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
> +	if (!hdr)
> +		goto err_free_msg;

ret is uninitialised here, but it is used in the error path.

This is flagged by a clang-16 W=1 build, and Smatch.

> +	ret = dpll_device_get_one(dpll, msg, NULL);
> +	if (ret)
> +		goto err_cancel_msg;
> +	genlmsg_end(msg, hdr);
> +	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
> +
> +	return 0;
> +
> +err_cancel_msg:
> +	genlmsg_cancel(msg, hdr);
> +err_free_msg:
> +	nlmsg_free(msg);
> +
> +	return ret;
> +}

...

> +int __dpll_device_change_ntf(struct dpll_device *dpll)
> +{
> +	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
> +}

Should this function be static?

...

> +static int
> +dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
> +{
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int ret;
> +
> +	if (WARN_ON(!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)))
> +		return -ENODEV;
> +
> +	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
> +	if (!hdr)
> +		goto err_free_msg;

It looks like ret is used uninitialised here too.

> +	ret = dpll_cmd_pin_get_one(msg, pin, NULL);
> +	if (ret)
> +		goto err_cancel_msg;
> +	genlmsg_end(msg, hdr);
> +	genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
> +
> +	return 0;
> +
> +err_cancel_msg:
> +	genlmsg_cancel(msg, hdr);
> +err_free_msg:
> +	nlmsg_free(msg);
> +
> +	return ret;
> +}

...

> +void
> +dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
> +		   struct genl_info *info)

The indentation of the line above is not correct.
There are two spaces too many.

...

> +void dpll_netlink_finish(void)
> +{
> +	genl_unregister_family(&dpll_nl_family);
> +}

Should this function be static?

...

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

* Re: [PATCH net-next 08/11] ice: add admin commands to access cgu configuration
  2023-07-20  9:19   ` Vadim Fedorenko
  (?)
@ 2023-07-24 17:21   ` Simon Horman
  2023-07-28 12:46       ` Kubalewski, Arkadiusz
  -1 siblings, 1 reply; 109+ messages in thread
From: Simon Horman @ 2023-07-24 17:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Thu, Jul 20, 2023 at 10:19:00AM +0100, Vadim Fedorenko wrote:

...

Hi Vadim,

> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c

...

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

./scripts/kernel-doc says that @config is missing here.

> + * @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,
> +			   u8 *dpll_state, u8 *config, s64 *phase_offset,
> +			   u8 *eec_mode)

...

> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c

...

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

Likewise, @eec_mode is missing here.

> + * @phase_offset: pointer to a buffer for returning phase offset
> + * @dpll_state: state of the DPLL (output)

And @mode is missing here.

> + *
> + * 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 dpll_lock_status last_dpll_state, u8 *pin,
> +		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
> +		      enum dpll_lock_status *dpll_state,
> +		      enum dpll_mode *mode)
> +{
> +	u8 hw_ref_state, hw_dpll_state, hw_eec_mode, hw_config;
> +	s64 hw_phase_offset;
> +	int status;
> +
> +	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
> +					    &hw_dpll_state, &hw_config,
> +					    &hw_phase_offset, &hw_eec_mode);
> +	if (status) {
> +		*dpll_state = ICE_CGU_STATE_INVALID;

dpll_state is of type enum dpll_lock_status.
But the type of ICE_CGU_STATE_INVALID is enum ice_cgu_state.
Is this intended?

As flagged by gcc-12 W=1 and clang-16 W=1 builds.

> +		return status;
> +	}
> +
> +	if (pin)
> +		/* current ref pin in dpll_state_refsel_status_X register */
> +		*pin = hw_config & ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL;
> +	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;

Here dpll_state is checked for NULL.
But, above, it is dereferenced in the case where ice_aq_get_cgu_dpll_status
fails. Is that safe?

Also, perhaps it makes things a bit clearer to return 0 here.

...

> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h

...

> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
> +};

A gcc-12 W=1 build warns that ice_e810t_sfp_cgu_inputs, and
the similar static variables below, are unused when ice_ptp_hw.h
is included in ice_main.c via ice.h.

Looking at ice_e823_zl_cgu_outputs[], it seems to only be used
in ice_ptp_hw.c, so perhaps it could be defined there.

Perhaps that is also true of the other static variables below,
but I didn't check that.

> +
> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
> +	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
> +	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
> +	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
> +	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
> +	{ "NONE",	  SI_REF0P, 0, 0 },
> +	{ "NONE",	  SI_REF0N, 0, 0 },
> +	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
> +	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
> +	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "NONE",	  SI_REF2N, 0, 0 },
> +	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
> +	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
> +	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
> +	{ "NONE",	  ZL_REF0P, 0, 0 },
> +	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
> +	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
> +	{ "NONE",	  ZL_REF2P, 0, 0 },
> +	{ "NONE",	  ZL_REF2N, 0, 0 },
> +	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "NONE",	  ZL_REF3N, 0, 0 },
> +	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
> +};
> +
> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
> +	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
> +	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
> +	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
> +	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
> +	{ "NONE",	   ZL_OUT5, 0, 0 },
> +};
> +
>  extern const struct
>  ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
>  

...

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-20  9:19   ` Vadim Fedorenko
                     ` (2 preceding siblings ...)
  (?)
@ 2023-07-24 17:41   ` Simon Horman
  2023-07-24 17:58     ` Vadim Fedorenko
  -1 siblings, 1 reply; 109+ messages in thread
From: Simon Horman @ 2023-07-24 17:41 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Thu, Jul 20, 2023 at 10:19:01AM +0100, Vadim Fedorenko wrote:

...

Hi Vadim,

> +/**
> + * ice_dpll_cb_unlock - unlock dplls mutex in callback context
> + * @pf: private board structure
> + *
> + * Unlock the mutex from the callback operations invoked by dpll subsystem.
> + */
> +static void ice_dpll_cb_unlock(struct ice_pf *pf)
> +{
> +	mutex_unlock(&pf->dplls.lock);
> +}
> +
> +/**
> + * ice_dpll_pin_freq_set - set pin's frequency
> + * @pf: private board structure
> + * @pin: pointer to a pin
> + * @pin_type: type of pin being configured
> + * @freq: frequency to be set
> + * @extack: error reporting
> + *
> + * Set requested frequency on a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error on AQ or wrong pin type given
> + */
> +static int
> +ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
> +		      enum ice_dpll_pin_type pin_type, const u32 freq,
> +		      struct netlink_ext_ack *extack)
> +{
> +	int ret;
> +	u8 flags;

Please arrange local variable declarations for new Networking
code in reverse xmas tree order - longest line to shortest.

> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
> +		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
> +					       pin->flags[0], freq, 0);
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
> +		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
> +						0, freq, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ret) {
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
> +				   ret,
> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
> +				   freq, pin->idx);
> +		return ret;
> +	}
> +	pin->freq = freq;
> +
> +	return 0;
> +}

...

> +/**
> + * ice_dpll_pin_state_update - update pin's state
> + * @pf: private board struct
> + * @pin: structure with pin attributes to be updated
> + * @pin_type: type of pin being updated
> + * @extack: error reporting
> + *
> + * Determine pin current state and frequency, then update struct
> + * holding the pin info. For input pin states are separated for each
> + * dpll, for rclk pins states are separated for each parent.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - OK
> + * * negative - error
> + */
> +int
> +ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
> +			  enum ice_dpll_pin_type pin_type,
> +			  struct netlink_ext_ack *extack)

> +/**
> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + * @pin_type: type of pin being configured
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Acquires pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +		       const struct dpll_device *dpll, void *dpll_priv,
> +		       const u32 frequency,
> +		       struct netlink_ext_ack *extack,
> +		       enum ice_dpll_pin_type pin_type)
> +{
> +	struct ice_dpll_pin *p = pin_priv;
> +	struct ice_dpll *d = dpll_priv;
> +	struct ice_pf *pf = d->pf;
> +	int ret;
> +
> +	ret = ice_dpll_cb_lock(pf, extack);
> +	if (ret)
> +		return ret;
> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
> +	ice_dpll_cb_unlock(pf);
> +
> +	return ret;
> +}
> +
> +/**
> + * ice_dpll_input_frequency_set - input pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +			     const struct dpll_device *dpll, void *dpll_priv,
> +			     u64 frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
> +}
> +
> +/**
> + * ice_dpll_output_frequency_set - output pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +			      const struct dpll_device *dpll, void *dpll_priv,
> +			      u64 frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
> +}
> +
> +/**
> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + * @pin_type: type of pin being configured
> + *
> + * Wraps internal get frequency command of a pin.
> + *
> + * Context: Acquires pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +		       const struct dpll_device *dpll, void *dpll_priv,
> +		       u64 *frequency, struct netlink_ext_ack *extack,
> +		       enum ice_dpll_pin_type pin_type)
> +{
> +	struct ice_dpll_pin *p = pin_priv;
> +	struct ice_dpll *d = dpll_priv;
> +	struct ice_pf *pf = d->pf;
> +	int ret;
> +
> +	ret = ice_dpll_cb_lock(pf, extack);
> +	if (ret)
> +		return ret;
> +	*frequency = p->freq;
> +	ice_dpll_cb_unlock(pf);
> +
> +	return 0;
> +}
> +
> +/**
> + * ice_dpll_input_frequency_get - input pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + *
> + * Wraps internal get frequency command of a input pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +			     const struct dpll_device *dpll, void *dpll_priv,
> +			     u64 *frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
> +}
> +
> +/**
> + * ice_dpll_output_frequency_get - output pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + *
> + * Wraps internal get frequency command of a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +			      const struct dpll_device *dpll, void *dpll_priv,
> +			      u64 *frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
> +}
> +
> +/**
> + * ice_dpll_pin_enable - enable a pin on dplls
> + * @hw: board private hw structure
> + * @pin: pointer to a pin
> + * @pin_type: type of pin being enabled
> + * @extack: error reporting
> + *
> + * Enable a pin on both dplls. Store current state in pin->flags.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - OK
> + * * negative - error
> + */
> +static int
> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
> +		    enum ice_dpll_pin_type pin_type,
> +		    struct netlink_ext_ack *extack)
> +{
> +	u8 flags = 0;
> +	int ret;
> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ret)
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to enable %s pin:%u\n",
> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
> +				   pin_type_name[pin_type], pin->idx);
> +
> +	return ret;
> +}
> +
> +/**
> + * ice_dpll_pin_disable - disable a pin on dplls
> + * @hw: board private hw structure
> + * @pin: pointer to a pin
> + * @pin_type: type of pin being disabled
> + * @extack: error reporting
> + *
> + * Disable a pin on both dplls. Store current state in pin->flags.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - OK
> + * * negative - error
> + */
> +static int
> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
> +		     enum ice_dpll_pin_type pin_type,
> +		     struct netlink_ext_ack *extack)
> +{
> +	u8 flags = 0;
> +	int ret;
> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ret)
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to disable %s pin:%u\n",
> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
> +				   pin_type_name[pin_type], pin->idx);
> +
> +	return ret;
> +}

> +/**
> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + * @pin_type: type of pin being configured
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Acquires pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +		       const struct dpll_device *dpll, void *dpll_priv,
> +		       const u32 frequency,
> +		       struct netlink_ext_ack *extack,
> +		       enum ice_dpll_pin_type pin_type)
> +{
> +	struct ice_dpll_pin *p = pin_priv;
> +	struct ice_dpll *d = dpll_priv;
> +	struct ice_pf *pf = d->pf;
> +	int ret;
> +
> +	ret = ice_dpll_cb_lock(pf, extack);
> +	if (ret)
> +		return ret;
> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
> +	ice_dpll_cb_unlock(pf);
> +
> +	return ret;
> +}
> +
> +/**
> + * ice_dpll_input_frequency_set - input pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +			     const struct dpll_device *dpll, void *dpll_priv,
> +			     u64 frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
> +}
> +
> +/**
> + * ice_dpll_output_frequency_set - output pin callback for set frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: frequency to be set
> + * @extack: error reporting
> + *
> + * Wraps internal set frequency command on a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't set in hw
> + */
> +static int
> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
> +			      const struct dpll_device *dpll, void *dpll_priv,
> +			      u64 frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
> +}
> +
> +/**
> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + * @pin_type: type of pin being configured
> + *
> + * Wraps internal get frequency command of a pin.
> + *
> + * Context: Acquires pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +		       const struct dpll_device *dpll, void *dpll_priv,
> +		       u64 *frequency, struct netlink_ext_ack *extack,
> +		       enum ice_dpll_pin_type pin_type)
> +{
> +	struct ice_dpll_pin *p = pin_priv;
> +	struct ice_dpll *d = dpll_priv;
> +	struct ice_pf *pf = d->pf;
> +	int ret;
> +
> +	ret = ice_dpll_cb_lock(pf, extack);
> +	if (ret)
> +		return ret;
> +	*frequency = p->freq;
> +	ice_dpll_cb_unlock(pf);
> +
> +	return 0;
> +}
> +
> +/**
> + * ice_dpll_input_frequency_get - input pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + *
> + * Wraps internal get frequency command of a input pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +			     const struct dpll_device *dpll, void *dpll_priv,
> +			     u64 *frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
> +}
> +
> +/**
> + * ice_dpll_output_frequency_get - output pin callback for get frequency
> + * @pin: pointer to a pin
> + * @pin_priv: private data pointer passed on pin registration
> + * @dpll: pointer to dpll
> + * @dpll_priv: private data pointer passed on dpll registration
> + * @frequency: on success holds pin's frequency
> + * @extack: error reporting
> + *
> + * Wraps internal get frequency command of a pin.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - error pin not found or couldn't get from hw
> + */
> +static int
> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
> +			      const struct dpll_device *dpll, void *dpll_priv,
> +			      u64 *frequency, struct netlink_ext_ack *extack)
> +{
> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
> +}
> +
> +/**
> + * ice_dpll_pin_enable - enable a pin on dplls
> + * @hw: board private hw structure
> + * @pin: pointer to a pin
> + * @pin_type: type of pin being enabled
> + * @extack: error reporting
> + *
> + * Enable a pin on both dplls. Store current state in pin->flags.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - OK
> + * * negative - error
> + */
> +static int
> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
> +		    enum ice_dpll_pin_type pin_type,
> +		    struct netlink_ext_ack *extack)
> +{
> +	u8 flags = 0;
> +	int ret;
> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ret)
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to enable %s pin:%u\n",
> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
> +				   pin_type_name[pin_type], pin->idx);
> +
> +	return ret;
> +}
> +
> +/**
> + * ice_dpll_pin_disable - disable a pin on dplls
> + * @hw: board private hw structure
> + * @pin: pointer to a pin
> + * @pin_type: type of pin being disabled
> + * @extack: error reporting
> + *
> + * Disable a pin on both dplls. Store current state in pin->flags.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - OK
> + * * negative - error
> + */
> +static int
> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
> +		     enum ice_dpll_pin_type pin_type,
> +		     struct netlink_ext_ack *extack)
> +{
> +	u8 flags = 0;
> +	int ret;
> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ret)
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to disable %s pin:%u\n",
> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
> +				   pin_type_name[pin_type], pin->idx);
> +
> +	return ret;
> +}

Should this function be static?

> +{
> +	int ret;
> +
> +	switch (pin_type) {
> +	case ICE_DPLL_PIN_TYPE_INPUT:
> +		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
> +					       NULL, &pin->flags[0],
> +					       &pin->freq, NULL);
> +		if (ret)
> +			goto err;
> +		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
> +			if (pin->pin) {
> +				pin->state[pf->dplls.eec.dpll_idx] =
> +					pin->pin == pf->dplls.eec.active_input ?
> +					DPLL_PIN_STATE_CONNECTED :
> +					DPLL_PIN_STATE_SELECTABLE;
> +				pin->state[pf->dplls.pps.dpll_idx] =
> +					pin->pin == pf->dplls.pps.active_input ?
> +					DPLL_PIN_STATE_CONNECTED :
> +					DPLL_PIN_STATE_SELECTABLE;
> +			} else {
> +				pin->state[pf->dplls.eec.dpll_idx] =
> +					DPLL_PIN_STATE_SELECTABLE;
> +				pin->state[pf->dplls.pps.dpll_idx] =
> +					DPLL_PIN_STATE_SELECTABLE;
> +			}
> +		} else {
> +			pin->state[pf->dplls.eec.dpll_idx] =
> +				DPLL_PIN_STATE_DISCONNECTED;
> +			pin->state[pf->dplls.pps.dpll_idx] =
> +				DPLL_PIN_STATE_DISCONNECTED;
> +		}
> +		break;
> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
> +		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
> +						&pin->flags[0], NULL,
> +						&pin->freq, NULL);
> +		if (ret)
> +			goto err;
> +		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
> +			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
> +		else
> +			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
> +		break;
> +	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:

clang-16 complains that:
 
  drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: expected expression
                  u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;

Which, I think means, it wants this case to be enclosed in { }

> +		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
> +
> +		for (parent = 0; parent < pf->dplls.rclk.num_parents;
> +		     parent++) {
> +			u8 p = parent;
> +
> +			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
> +							 &port_num,
> +							 &pin->flags[parent],
> +							 NULL);
> +			if (ret)
> +				goto err;
> +			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
> +			    pin->flags[parent])
> +				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
> +			else
> +				pin->state[parent] =
> +					DPLL_PIN_STATE_DISCONNECTED;
> +		}
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +err:
> +	if (extack)
> +		NL_SET_ERR_MSG_FMT(extack,
> +				   "err:%d %s failed to update %s pin:%u\n",
> +				   ret,
> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
> +				   pin_type_name[pin_type], pin->idx);
> +	else
> +		dev_err_ratelimited(ice_pf_to_dev(pf),
> +				    "err:%d %s failed to update %s pin:%u\n",
> +				    ret,
> +				    ice_aq_str(pf->hw.adminq.sq_last_status),
> +				    pin_type_name[pin_type], pin->idx);
> +	return ret;
> +}

...

> +/**
> + * ice_dpll_update_state - update dpll state
> + * @pf: pf private structure
> + * @d: pointer to queried dpll device
> + * @init: if function called on initialization of ice dpll
> + *
> + * Poll current state of dpll from hw and update ice_dpll struct.
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - AQ failure
> + */
> +static int
> +ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
> +{
> +	struct ice_dpll_pin *p = NULL;
> +	int ret;
> +
> +	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
> +				&d->input_idx, &d->ref_state, &d->eec_mode,
> +				&d->phase_shift, &d->dpll_state, &d->mode);
> +
> +	dev_dbg(ice_pf_to_dev(pf),
> +		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
> +		d->dpll_idx, d->prev_input_idx, d->input_idx,
> +		d->dpll_state, d->prev_dpll_state, d->mode);
> +	if (ret) {
> +		dev_err(ice_pf_to_dev(pf),
> +			"update dpll=%d state failed, ret=%d %s\n",
> +			d->dpll_idx, ret,
> +			ice_aq_str(pf->hw.adminq.sq_last_status));
> +		return ret;
> +	}
> +	if (init) {
> +		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
> +		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)

Should this be '||' rather than '&&' ?

Flagged by a clang-16 W=1 build, Sparse and Smatch.

> +			d->active_input = pf->dplls.inputs[d->input_idx].pin;
> +		p = &pf->dplls.inputs[d->input_idx];
> +		return ice_dpll_pin_state_update(pf, p,
> +						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
> +	}

...

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

I'm unsure if this can happen,
but if the for loop above iterates zero times
then ret will be null here.

Use of uninitialised variable flagged by Smatch.

> +	return ret;
> +}

...

> +/**
> + * 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).
> + *
> + * Context: Called under pf->dplls.lock
> + * Return:
> + * * 0 - success
> + * * negative - init failure reason
> + */
> +static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
> +{
> +	struct ice_aqc_get_cgu_abilities abilities;
> +	struct ice_dpll *de = &pf->dplls.eec;
> +	struct ice_dpll *dp = &pf->dplls.pps;
> +	struct ice_dplls *d = &pf->dplls;
> +	struct ice_hw *hw = &pf->hw;
> +	int ret, alloc_size, i;
> +
> +	d->clock_id = ice_generate_clock_id(pf);
> +	ret = ice_aq_get_cgu_abilities(hw, &abilities);
> +	if (ret) {
> +		dev_err(ice_pf_to_dev(pf),
> +			"err:%d %s failed to read cgu abilities\n",
> +			ret, ice_aq_str(hw->adminq.sq_last_status));
> +		return ret;
> +	}
> +
> +	de->dpll_idx = abilities.eec_dpll_idx;
> +	dp->dpll_idx = abilities.pps_dpll_idx;
> +	d->num_inputs = abilities.num_inputs;
> +	d->num_outputs = abilities.num_outputs;
> +	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
> +	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
> +
> +	alloc_size = sizeof(*d->inputs) * d->num_inputs;
> +	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
> +	if (!d->inputs)
> +		return -ENOMEM;
> +
> +	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
> +	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
> +	if (!de->input_prio)
> +		return -ENOMEM;
> +
> +	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
> +	if (!dp->input_prio)
> +		return -ENOMEM;
> +
> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
> +	if (ret)
> +		goto deinit_info;
> +
> +	if (cgu) {
> +		alloc_size = sizeof(*d->outputs) * d->num_outputs;
> +		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
> +		if (!d->outputs)

Should ret be set to -ENOMEM here?

Flagged by Smatch.

> +			goto deinit_info;
> +
> +		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
> +		if (ret)
> +			goto deinit_info;
> +	}
> +
> +	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
> +					&pf->dplls.rclk.num_parents);
> +	if (ret)
> +		return ret;
> +	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
> +		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
> +	if (ret)
> +		return ret;
> +	de->mode = DPLL_MODE_AUTOMATIC;
> +	dp->mode = DPLL_MODE_AUTOMATIC;
> +
> +	dev_dbg(ice_pf_to_dev(pf),
> +		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
> +		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
> +
> +	return 0;
> +
> +deinit_info:
> +	dev_err(ice_pf_to_dev(pf),
> +		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
> +		__func__, d->inputs, de->input_prio,
> +		dp->input_prio, d->outputs);
> +	ice_dpll_deinit_info(pf);
> +	return ret;
> +}

...

> +/**
> + * ice_dpll_init - initialize support for dpll subsystem
> + * @pf: board private structure
> + *
> + * Set up the device dplls, register them and pins connected within Linux dpll
> + * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL

nit: userpsace -> userspace

> + * configuration requests.
> + *
> + * Context: Function initializes and holds pf->dplls.lock mutex.
> + */

...

> 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..975066b71c5e
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2022, Intel Corporation. */
> +
> +#ifndef _ICE_DPLL_H_
> +#define _ICE_DPLL_H_
> +
> +#include "ice.h"
> +
> +#define ICE_DPLL_PRIO_MAX	0xF
> +#define ICE_DPLL_RCLK_NUM_MAX	4
> +
> +/** ice_dpll_pin - store info about pins
> + * @pin: dpll pin structure
> + * @pf: pointer to pf, which has registered the dpll_pin
> + * @idx: ice pin private idx
> + * @num_parents: hols number of parent pins
> + * @parent_idx: hold indexes of parent pins
> + * @flags: pin flags returned from HW
> + * @state: state of a pin
> + * @prop: pin properities

nit: properities -> properties

> + * @freq: current frequency of a pin
> + */
> +struct ice_dpll_pin {
> +	struct dpll_pin *pin;
> +	struct ice_pf *pf;
> +	u8 idx;
> +	u8 num_parents;
> +	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
> +	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
> +	u8 state[ICE_DPLL_RCLK_NUM_MAX];
> +	struct dpll_pin_properties prop;
> +	u32 freq;
> +};

...

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-24 17:41   ` Simon Horman
@ 2023-07-24 17:58     ` Vadim Fedorenko
  2023-07-28 23:10         ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-24 17:58 UTC (permalink / raw)
  To: Simon Horman, Arkadiusz Kubalewski
  Cc: Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni,
	Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

On 24.07.2023 18:41, Simon Horman wrote:
> On Thu, Jul 20, 2023 at 10:19:01AM +0100, Vadim Fedorenko wrote:
> 
> ...
> 
> Hi Vadim,
> 

Hi Simon!
Thanks for the review. I believe Arkadiusz as the author of the patch will
adjust the code accordingly

>> +/**
>> + * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>> + * @pf: private board structure
>> + *
>> + * Unlock the mutex from the callback operations invoked by dpll subsystem.
>> + */
>> +static void ice_dpll_cb_unlock(struct ice_pf *pf)
>> +{
>> +	mutex_unlock(&pf->dplls.lock);
>> +}
>> +
>> +/**
>> + * ice_dpll_pin_freq_set - set pin's frequency
>> + * @pf: private board structure
>> + * @pin: pointer to a pin
>> + * @pin_type: type of pin being configured
>> + * @freq: frequency to be set
>> + * @extack: error reporting
>> + *
>> + * Set requested frequency on a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error on AQ or wrong pin type given
>> + */
>> +static int
>> +ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>> +		      enum ice_dpll_pin_type pin_type, const u32 freq,
>> +		      struct netlink_ext_ack *extack)
>> +{
>> +	int ret;
>> +	u8 flags;
> 
> Please arrange local variable declarations for new Networking
> code in reverse xmas tree order - longest line to shortest.
> 
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>> +		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>> +					       pin->flags[0], freq, 0);
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>> +		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>> +						0, freq, 0);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ret) {
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to set pin freq:%u on pin:%u\n",
>> +				   ret,
>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>> +				   freq, pin->idx);
>> +		return ret;
>> +	}
>> +	pin->freq = freq;
>> +
>> +	return 0;
>> +}
> 
> ...
> 
>> +/**
>> + * ice_dpll_pin_state_update - update pin's state
>> + * @pf: private board struct
>> + * @pin: structure with pin attributes to be updated
>> + * @pin_type: type of pin being updated
>> + * @extack: error reporting
>> + *
>> + * Determine pin current state and frequency, then update struct
>> + * holding the pin info. For input pin states are separated for each
>> + * dpll, for rclk pins states are separated for each parent.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - OK
>> + * * negative - error
>> + */
>> +int
>> +ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>> +			  enum ice_dpll_pin_type pin_type,
>> +			  struct netlink_ext_ack *extack)
> 
>> +/**
>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + * @pin_type: type of pin being configured
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Acquires pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +		       const struct dpll_device *dpll, void *dpll_priv,
>> +		       const u32 frequency,
>> +		       struct netlink_ext_ack *extack,
>> +		       enum ice_dpll_pin_type pin_type)
>> +{
>> +	struct ice_dpll_pin *p = pin_priv;
>> +	struct ice_dpll *d = dpll_priv;
>> +	struct ice_pf *pf = d->pf;
>> +	int ret;
>> +
>> +	ret = ice_dpll_cb_lock(pf, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>> +	ice_dpll_cb_unlock(pf);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +			     const struct dpll_device *dpll, void *dpll_priv,
>> +			     u64 frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_output_frequency_set - output pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +			      const struct dpll_device *dpll, void *dpll_priv,
>> +			      u64 frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + * @pin_type: type of pin being configured
>> + *
>> + * Wraps internal get frequency command of a pin.
>> + *
>> + * Context: Acquires pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +		       const struct dpll_device *dpll, void *dpll_priv,
>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>> +		       enum ice_dpll_pin_type pin_type)
>> +{
>> +	struct ice_dpll_pin *p = pin_priv;
>> +	struct ice_dpll *d = dpll_priv;
>> +	struct ice_pf *pf = d->pf;
>> +	int ret;
>> +
>> +	ret = ice_dpll_cb_lock(pf, extack);
>> +	if (ret)
>> +		return ret;
>> +	*frequency = p->freq;
>> +	ice_dpll_cb_unlock(pf);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + *
>> + * Wraps internal get frequency command of a input pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +			     const struct dpll_device *dpll, void *dpll_priv,
>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_output_frequency_get - output pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + *
>> + * Wraps internal get frequency command of a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +			      const struct dpll_device *dpll, void *dpll_priv,
>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_pin_enable - enable a pin on dplls
>> + * @hw: board private hw structure
>> + * @pin: pointer to a pin
>> + * @pin_type: type of pin being enabled
>> + * @extack: error reporting
>> + *
>> + * Enable a pin on both dplls. Store current state in pin->flags.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - OK
>> + * * negative - error
>> + */
>> +static int
>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>> +		    enum ice_dpll_pin_type pin_type,
>> +		    struct netlink_ext_ack *extack)
>> +{
>> +	u8 flags = 0;
>> +	int ret;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ret)
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to enable %s pin:%u\n",
>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>> +				   pin_type_name[pin_type], pin->idx);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * ice_dpll_pin_disable - disable a pin on dplls
>> + * @hw: board private hw structure
>> + * @pin: pointer to a pin
>> + * @pin_type: type of pin being disabled
>> + * @extack: error reporting
>> + *
>> + * Disable a pin on both dplls. Store current state in pin->flags.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - OK
>> + * * negative - error
>> + */
>> +static int
>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>> +		     enum ice_dpll_pin_type pin_type,
>> +		     struct netlink_ext_ack *extack)
>> +{
>> +	u8 flags = 0;
>> +	int ret;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ret)
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to disable %s pin:%u\n",
>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>> +				   pin_type_name[pin_type], pin->idx);
>> +
>> +	return ret;
>> +}
> 
>> +/**
>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + * @pin_type: type of pin being configured
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Acquires pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +		       const struct dpll_device *dpll, void *dpll_priv,
>> +		       const u32 frequency,
>> +		       struct netlink_ext_ack *extack,
>> +		       enum ice_dpll_pin_type pin_type)
>> +{
>> +	struct ice_dpll_pin *p = pin_priv;
>> +	struct ice_dpll *d = dpll_priv;
>> +	struct ice_pf *pf = d->pf;
>> +	int ret;
>> +
>> +	ret = ice_dpll_cb_lock(pf, extack);
>> +	if (ret)
>> +		return ret;
>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>> +	ice_dpll_cb_unlock(pf);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +			     const struct dpll_device *dpll, void *dpll_priv,
>> +			     u64 frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_output_frequency_set - output pin callback for set frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: frequency to be set
>> + * @extack: error reporting
>> + *
>> + * Wraps internal set frequency command on a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't set in hw
>> + */
>> +static int
>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>> +			      const struct dpll_device *dpll, void *dpll_priv,
>> +			      u64 frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + * @pin_type: type of pin being configured
>> + *
>> + * Wraps internal get frequency command of a pin.
>> + *
>> + * Context: Acquires pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +		       const struct dpll_device *dpll, void *dpll_priv,
>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>> +		       enum ice_dpll_pin_type pin_type)
>> +{
>> +	struct ice_dpll_pin *p = pin_priv;
>> +	struct ice_dpll *d = dpll_priv;
>> +	struct ice_pf *pf = d->pf;
>> +	int ret;
>> +
>> +	ret = ice_dpll_cb_lock(pf, extack);
>> +	if (ret)
>> +		return ret;
>> +	*frequency = p->freq;
>> +	ice_dpll_cb_unlock(pf);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + *
>> + * Wraps internal get frequency command of a input pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +			     const struct dpll_device *dpll, void *dpll_priv,
>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_output_frequency_get - output pin callback for get frequency
>> + * @pin: pointer to a pin
>> + * @pin_priv: private data pointer passed on pin registration
>> + * @dpll: pointer to dpll
>> + * @dpll_priv: private data pointer passed on dpll registration
>> + * @frequency: on success holds pin's frequency
>> + * @extack: error reporting
>> + *
>> + * Wraps internal get frequency command of a pin.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - error pin not found or couldn't get from hw
>> + */
>> +static int
>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>> +			      const struct dpll_device *dpll, void *dpll_priv,
>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>> +{
>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>> +}
>> +
>> +/**
>> + * ice_dpll_pin_enable - enable a pin on dplls
>> + * @hw: board private hw structure
>> + * @pin: pointer to a pin
>> + * @pin_type: type of pin being enabled
>> + * @extack: error reporting
>> + *
>> + * Enable a pin on both dplls. Store current state in pin->flags.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - OK
>> + * * negative - error
>> + */
>> +static int
>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>> +		    enum ice_dpll_pin_type pin_type,
>> +		    struct netlink_ext_ack *extack)
>> +{
>> +	u8 flags = 0;
>> +	int ret;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ret)
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to enable %s pin:%u\n",
>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>> +				   pin_type_name[pin_type], pin->idx);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * ice_dpll_pin_disable - disable a pin on dplls
>> + * @hw: board private hw structure
>> + * @pin: pointer to a pin
>> + * @pin_type: type of pin being disabled
>> + * @extack: error reporting
>> + *
>> + * Disable a pin on both dplls. Store current state in pin->flags.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - OK
>> + * * negative - error
>> + */
>> +static int
>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>> +		     enum ice_dpll_pin_type pin_type,
>> +		     struct netlink_ext_ack *extack)
>> +{
>> +	u8 flags = 0;
>> +	int ret;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ret)
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to disable %s pin:%u\n",
>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>> +				   pin_type_name[pin_type], pin->idx);
>> +
>> +	return ret;
>> +}
> 
> Should this function be static?
> 
>> +{
>> +	int ret;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>> +					       NULL, &pin->flags[0],
>> +					       &pin->freq, NULL);
>> +		if (ret)
>> +			goto err;
>> +		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>> +			if (pin->pin) {
>> +				pin->state[pf->dplls.eec.dpll_idx] =
>> +					pin->pin == pf->dplls.eec.active_input ?
>> +					DPLL_PIN_STATE_CONNECTED :
>> +					DPLL_PIN_STATE_SELECTABLE;
>> +				pin->state[pf->dplls.pps.dpll_idx] =
>> +					pin->pin == pf->dplls.pps.active_input ?
>> +					DPLL_PIN_STATE_CONNECTED :
>> +					DPLL_PIN_STATE_SELECTABLE;
>> +			} else {
>> +				pin->state[pf->dplls.eec.dpll_idx] =
>> +					DPLL_PIN_STATE_SELECTABLE;
>> +				pin->state[pf->dplls.pps.dpll_idx] =
>> +					DPLL_PIN_STATE_SELECTABLE;
>> +			}
>> +		} else {
>> +			pin->state[pf->dplls.eec.dpll_idx] =
>> +				DPLL_PIN_STATE_DISCONNECTED;
>> +			pin->state[pf->dplls.pps.dpll_idx] =
>> +				DPLL_PIN_STATE_DISCONNECTED;
>> +		}
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>> +						&pin->flags[0], NULL,
>> +						&pin->freq, NULL);
>> +		if (ret)
>> +			goto err;
>> +		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>> +			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>> +		else
>> +			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
> 
> clang-16 complains that:
>   
>    drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: expected expression
>                    u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
> 
> Which, I think means, it wants this case to be enclosed in { }
> 
>> +		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>> +
>> +		for (parent = 0; parent < pf->dplls.rclk.num_parents;
>> +		     parent++) {
>> +			u8 p = parent;
>> +
>> +			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
>> +							 &port_num,
>> +							 &pin->flags[parent],
>> +							 NULL);
>> +			if (ret)
>> +				goto err;
>> +			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>> +			    pin->flags[parent])
>> +				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>> +			else
>> +				pin->state[parent] =
>> +					DPLL_PIN_STATE_DISCONNECTED;
>> +		}
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	if (extack)
>> +		NL_SET_ERR_MSG_FMT(extack,
>> +				   "err:%d %s failed to update %s pin:%u\n",
>> +				   ret,
>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>> +				   pin_type_name[pin_type], pin->idx);
>> +	else
>> +		dev_err_ratelimited(ice_pf_to_dev(pf),
>> +				    "err:%d %s failed to update %s pin:%u\n",
>> +				    ret,
>> +				    ice_aq_str(pf->hw.adminq.sq_last_status),
>> +				    pin_type_name[pin_type], pin->idx);
>> +	return ret;
>> +}
> 
> ...
> 
>> +/**
>> + * ice_dpll_update_state - update dpll state
>> + * @pf: pf private structure
>> + * @d: pointer to queried dpll device
>> + * @init: if function called on initialization of ice dpll
>> + *
>> + * Poll current state of dpll from hw and update ice_dpll struct.
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - AQ failure
>> + */
>> +static int
>> +ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>> +{
>> +	struct ice_dpll_pin *p = NULL;
>> +	int ret;
>> +
>> +	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>> +				&d->input_idx, &d->ref_state, &d->eec_mode,
>> +				&d->phase_shift, &d->dpll_state, &d->mode);
>> +
>> +	dev_dbg(ice_pf_to_dev(pf),
>> +		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
>> +		d->dpll_idx, d->prev_input_idx, d->input_idx,
>> +		d->dpll_state, d->prev_dpll_state, d->mode);
>> +	if (ret) {
>> +		dev_err(ice_pf_to_dev(pf),
>> +			"update dpll=%d state failed, ret=%d %s\n",
>> +			d->dpll_idx, ret,
>> +			ice_aq_str(pf->hw.adminq.sq_last_status));
>> +		return ret;
>> +	}
>> +	if (init) {
>> +		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
>> +		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
> 
> Should this be '||' rather than '&&' ?
> 
> Flagged by a clang-16 W=1 build, Sparse and Smatch.
> 
>> +			d->active_input = pf->dplls.inputs[d->input_idx].pin;
>> +		p = &pf->dplls.inputs[d->input_idx];
>> +		return ice_dpll_pin_state_update(pf, p,
>> +						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
>> +	}
> 
> ...
> 
>> +/**
>> + * ice_dpll_init_info_direct_pins - initializes direct pins info
>> + * @pf: board private structure
>> + * @pin_type: type of pins being initialized
>> + *
>> + * Init information for directly connected pins, cache them in pf's pins
>> + * structures.
>> + *
>> + * Context: Called under pf->dplls.lock.
>> + * Return:
>> + * * 0 - success
>> + * * negative - init failure reason
>> + */
>> +static int
>> +ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>> +			       enum ice_dpll_pin_type pin_type)
>> +{
>> +	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>> +	struct ice_hw *hw = &pf->hw;
>> +	struct ice_dpll_pin *pins;
>> +	int num_pins, i, ret;
>> +	u8 freq_supp_num;
>> +	bool input;
>> +
>> +	switch (pin_type) {
>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>> +		pins = pf->dplls.inputs;
>> +		num_pins = pf->dplls.num_inputs;
>> +		input = true;
>> +		break;
>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>> +		pins = pf->dplls.outputs;
>> +		num_pins = pf->dplls.num_outputs;
>> +		input = false;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	for (i = 0; i < num_pins; i++) {
>> +		pins[i].idx = i;
>> +		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>> +		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>> +		if (input) {
>> +			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>> +						      &de->input_prio[i]);
>> +			if (ret)
>> +				return ret;
>> +			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>> +						      &dp->input_prio[i]);
>> +			if (ret)
>> +				return ret;
>> +			pins[i].prop.capabilities |=
>> +				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>> +		}
>> +		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>> +		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>> +		if (ret)
>> +			return ret;
>> +		pins[i].prop.freq_supported =
>> +			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>> +		pins[i].prop.freq_supported_num = freq_supp_num;
>> +		pins[i].pf = pf;
>> +	}
>> +
> 
> I'm unsure if this can happen,
> but if the for loop above iterates zero times
> then ret will be null here.
> 
> Use of uninitialised variable flagged by Smatch.
> 
>> +	return ret;
>> +}
> 
> ...
> 
>> +/**
>> + * 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).
>> + *
>> + * Context: Called under pf->dplls.lock
>> + * Return:
>> + * * 0 - success
>> + * * negative - init failure reason
>> + */
>> +static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>> +{
>> +	struct ice_aqc_get_cgu_abilities abilities;
>> +	struct ice_dpll *de = &pf->dplls.eec;
>> +	struct ice_dpll *dp = &pf->dplls.pps;
>> +	struct ice_dplls *d = &pf->dplls;
>> +	struct ice_hw *hw = &pf->hw;
>> +	int ret, alloc_size, i;
>> +
>> +	d->clock_id = ice_generate_clock_id(pf);
>> +	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>> +	if (ret) {
>> +		dev_err(ice_pf_to_dev(pf),
>> +			"err:%d %s failed to read cgu abilities\n",
>> +			ret, ice_aq_str(hw->adminq.sq_last_status));
>> +		return ret;
>> +	}
>> +
>> +	de->dpll_idx = abilities.eec_dpll_idx;
>> +	dp->dpll_idx = abilities.pps_dpll_idx;
>> +	d->num_inputs = abilities.num_inputs;
>> +	d->num_outputs = abilities.num_outputs;
>> +	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>> +	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>> +
>> +	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>> +	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>> +	if (!d->inputs)
>> +		return -ENOMEM;
>> +
>> +	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>> +	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>> +	if (!de->input_prio)
>> +		return -ENOMEM;
>> +
>> +	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>> +	if (!dp->input_prio)
>> +		return -ENOMEM;
>> +
>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>> +	if (ret)
>> +		goto deinit_info;
>> +
>> +	if (cgu) {
>> +		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>> +		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>> +		if (!d->outputs)
> 
> Should ret be set to -ENOMEM here?
> 
> Flagged by Smatch.
> 
>> +			goto deinit_info;
>> +
>> +		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>> +		if (ret)
>> +			goto deinit_info;
>> +	}
>> +
>> +	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>> +					&pf->dplls.rclk.num_parents);
>> +	if (ret)
>> +		return ret;
>> +	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>> +		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>> +	if (ret)
>> +		return ret;
>> +	de->mode = DPLL_MODE_AUTOMATIC;
>> +	dp->mode = DPLL_MODE_AUTOMATIC;
>> +
>> +	dev_dbg(ice_pf_to_dev(pf),
>> +		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>> +		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>> +
>> +	return 0;
>> +
>> +deinit_info:
>> +	dev_err(ice_pf_to_dev(pf),
>> +		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>> +		__func__, d->inputs, de->input_prio,
>> +		dp->input_prio, d->outputs);
>> +	ice_dpll_deinit_info(pf);
>> +	return ret;
>> +}
> 
> ...
> 
>> +/**
>> + * ice_dpll_init - initialize support for dpll subsystem
>> + * @pf: board private structure
>> + *
>> + * Set up the device dplls, register them and pins connected within Linux dpll
>> + * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
> 
> nit: userpsace -> userspace
> 
>> + * configuration requests.
>> + *
>> + * Context: Function initializes and holds pf->dplls.lock mutex.
>> + */
> 
> ...
> 
>> 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..975066b71c5e
>> --- /dev/null
>> +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>> @@ -0,0 +1,104 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (C) 2022, Intel Corporation. */
>> +
>> +#ifndef _ICE_DPLL_H_
>> +#define _ICE_DPLL_H_
>> +
>> +#include "ice.h"
>> +
>> +#define ICE_DPLL_PRIO_MAX	0xF
>> +#define ICE_DPLL_RCLK_NUM_MAX	4
>> +
>> +/** ice_dpll_pin - store info about pins
>> + * @pin: dpll pin structure
>> + * @pf: pointer to pf, which has registered the dpll_pin
>> + * @idx: ice pin private idx
>> + * @num_parents: hols number of parent pins
>> + * @parent_idx: hold indexes of parent pins
>> + * @flags: pin flags returned from HW
>> + * @state: state of a pin
>> + * @prop: pin properities
> 
> nit: properities -> properties
> 
>> + * @freq: current frequency of a pin
>> + */
>> +struct ice_dpll_pin {
>> +	struct dpll_pin *pin;
>> +	struct ice_pf *pf;
>> +	u8 idx;
>> +	u8 num_parents;
>> +	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>> +	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>> +	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>> +	struct dpll_pin_properties prop;
>> +	u32 freq;
>> +};
> 
> ...


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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-24 15:03                     ` Kubalewski, Arkadiusz
@ 2023-07-25  8:03                       ` Jiri Pirko
  2023-07-25 14:01                         ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 109+ messages in thread
From: Jiri Pirko @ 2023-07-25  8:03 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Mon, Jul 24, 2023 at 05:03:55PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Saturday, July 22, 2023 8:37 AM
>>
>>Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 5:46 PM
>>>>
>>>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>>>
>>>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>>>
>>>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>>wrote:
>>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>>>
>>>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>>>wrote:
>>>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>>>
>>>>>>>>>>[...]
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>+/**
>>>>>>>>>>>+ * 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
>>>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>>>+ *
>>>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>>>>>>>>>>+ *
>>>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>>>+ * Return:
>>>>>>>>>>>+ * * 0 - OK
>>>>>>>>>>>+ * * negative - error
>>>>>>>>>>>+ */
>>>>>>>>>>>+static int
>>>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>>>+{
>>>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>>>+	int ret;
>>>>>>>>>>>+
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun here or
>>>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Because you are probably still thinking the modes are somehow connected
>>>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>>>The dpll device mode is a state of DPLL before pins are even
>>>>>>>>>considered.
>>>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>>>monitor
>>>>>>>>>any of the pins.
>>>>>>>>>
>>>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>>>patchset any description about why we need the freerun mode. What is
>>>>>>>>>>diffrerent between:
>>>>>>>>>>1) freerun mode
>>>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>>>
>>>>>>>>>The difference:
>>>>>>>>>Case I:
>>>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>>>AUTOMATIC
>>>>>>>>>2. switch to AUTOMATIC
>>>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>>>
>>>>>>>>>Case II:
>>>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>>>
>>>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>>>they
>>>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>>>monitoring,
>>>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>>>longer as
>>>>>>>>>dpll need to start the process from scratch.
>>>>>>>>
>>>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>>>
>>>>>>>
>>>>>>>And we will have the same discussion later.. But implementation is
>>>>>>>already
>>>>>>>there.
>>>>>>
>>>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>>>merged, so anything which is blocking us and is totally optional (as
>>>>>>this freerun mode) is better to be dropped.
>>>>>>
>>>>>
>>>>>It is not blocking anything. Most of it was defined and available for
>>>>>long time already. Only ice implementing set_mode is a new part.
>>>>>No clue what is the problem you are implying here.
>>>>
>>>>Problem is that I believe you freerun mode should not exist. I believe
>>>>it is wrong.
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>As said in our previous discussion, without mode_set there is no point to
>>>>>>>have
>>>>>>>command DEVICE_SET at all, and there you said that you are ok with having
>>>>>>>the
>>>>>>>command as a placeholder, which doesn't make sense, since it is not used.
>>>>>>
>>>>>>I don't see any problem in having enum value reserved. But it does not
>>>>>>need to be there at all. You can add it to the end of the list when
>>>>>>needed. No problem. This is not an argument.
>>>>>>
>>>>>
>>>>>The argument is that I already implemented and tested, and have the need
>>>>>for the
>>>>>existence to set_mode to configure DPLL, which is there to switch the mode
>>>>>between AUTOMATIC and FREERUN.
>>>>>
>>>>>>
>>>>>>>
>>>>>>>Also this is not HW implementation detail but a synchronizer chip
>>>>>>>feature,
>>>>>>>once dpll is in FREERUN mode, the measurements like phase offset between
>>>>>>>the
>>>>>>>input and dpll's output won't be available.
>>>>>>>
>>>>>>>For the user there is a difference..
>>>>>>>Enabling the FREERUN mode is a reset button on the dpll's state machine,
>>>>>>>where disconnecting sources is not, as they are still used, monitored and
>>>>>>>measured.
>>>>>>
>>>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>>>have a state to indicate the state of the state machine (unlocked, locked,
>>>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>>>
>>>>>>Please don't mix config and state. I think we untangled this in the past
>>>>>>:/
>>>>>
>>>>>I don't mix anything, this is the way dpll works, which means mode of dpll.
>>>>
>>>>You do. You want to force-change the state yet you mangle the mode in.
>>>>The fact that some specific dpll implemented it as mode does not mean it
>>>>has to be exposed like that to user. We have to find the right
>>>>abstraction.
>>>>
>>>
>>>Just to make it clear:
>>>
>>>AUTOMATIC:
>>>- inputs monitored, validated, phase measurements available
>>>- possible states: unlocked, locked, locked-ho-acq, holdover
>>>
>>>FREERUN:
>>>- inputs not monitored, not validated, no phase measurements available
>>>- possible states: unlocked
>>
>>This is your implementation of DPLL. Others may have it done
>>differently. But the fact the input is monitored or not, does not make
>>any difference from user perspective.
>>
>>When he has automatic mode and does:
>>1) disconnect all pins
>>2) reset state    (however you implement it in the driver is totaly up
>>		   to the device, you may go to your freerun dpll mode
>>		   internally and to automatic back, up to you)
>> -> state will go to unlocked
>>
>>The behaviour is exactly the same, without any special mode.
>
>In this case there is special reset button, which doesn't exist in
>reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>to pretend the some kind of reset has happened, where in reality dpll went to
>FREERUN and AUTOMATIC.

There are 3 pin states:
disconnected
connected
selectable

When the last source disconnects, go to your internal freerun.
When some source gets selectable or connected, go to your internal
automatic mode.

From user perspactive, the mode didn't change.

From user perepective, this is exacly the behaviour he requested.


>For me it seems it seems like unnecessary complication of user's life.
>The idea of FREERUN mode is to run dpll on its system clock, so all the
>"external" dpll sources shall be disconnected when dpll is in FREERUN.

Yes, that is when you set all pins to disconnect. no mode change needed.


>Let's assume your HW doesn't have a FREERUN, can't you just create it by
>disconnecting all the sources? 

Yep, that's what we do.


>BTW, what chip are you using on mlx5 for this?
>I don't understand why the user would have to mangle state of all the pins just
>to stop dpll's work if he could just go into FREERUN and voila. Also what if
>user doesn't want change the configuration of the pins at all, and he just want
>to desynchronize it's dpll for i.e. testing reason.

I tried to explain multiple times. Let the user have clean an abstracted
api, with clear semantics. Simple as that. Your internal freerun mode is
just something to abstract out, it is not needed to expose it.


>
>>
>>We are talking about UAPI here. It should provide the abstraction, leaving
>>the
>>internal implementation behind the curtain. What is important is:
>>1) clear configuration knobs
>>2) the outcome (hw behaviour)
>>
>>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>>>to hit this button.
>>>>>>
>>>>>
>>>>>As already said there are measurement in place in AUTOMATIC, there are no
>>>>>such
>>>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>>>which
>>>>>is a side effect of going to FREERUN.
>>>>>
>>>>>>
>>>>>>
>>>>>>>So probably most important fact that you are missing here: assuming the
>>>>>>>user
>>>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>>>UNLOCKED
>>>>>>>state but into HOLDOVER.
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why? This
>>>>>>>>>>needs to be documented, please.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>>>
>>>>>>>>No, please drop it from this patchset. I have no clue why you readded
>>>>>>>>it in the first place in the last patchset version.
>>>>>>>>
>>>>>>>
>>>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>>>driver
>>>>>>>as it should.
>>>>>>
>>>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>>>remove it. It can be easily added when needed. No problem.
>>>>>>
>>>>>
>>>>>Well, I don't understand the fixation about removing it.
>>>>
>>>>It is needed only for your freerun mode, which is questionable. This
>>>>discussion it not about mode_set. I don't care about it, if it is
>>>>needed, should be there, if not, so be it.
>>>>
>>>>As you say, you need existance of your freerun mode to justify existence
>>>>of mode_set(). Could you please, please drop both for now so we can
>>>>move on? I'm tired of this. Thanks!
>>>>
>>>
>>>Reason for dpll subsystem is to control the dpll. So the mode_set and
>>>different modes are there for the same reason.
>>>Explained this multiple times already, we need a way to let the user switch
>>>to FREERUN, so all the activities on dpll are stopped.
>>>
>>>>
>>>>>set_mode was there for a long time, now the callback is properly
>>>>>implemented
>>>>>and you are trying to imply that this is not needed.
>>>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>>>to do its work.
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>>>connected with a single DPLL pin:
>>>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>>>
>>>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>>>
>>>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>>>
>>>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>For mlx5 it goes like:
>>>>>>>>>>
>>>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>>>1)
>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>>>    -> pin_id: 10
>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>>>    -> device_id: 2
>>>>>>>>>
>>>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it doesn't
>>>>>>>>>relate to the dpll interface..
>>>>>>>>
>>>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as input.
>>>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>>>
>>>>>>>
>>>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon shall
>>>>>>>know already something about the dpll it is managing.
>>>>>>>Not saying it is not needed, I am saying this is not a moment the SyncE
>>>>>>>daemon
>>>>>>>learns it.
>>>>>>
>>>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>>>
>>>>>>
>>>>>>>But let's park it, as this is not really relevant.
>>>>>>
>>>>>>Agreed.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>>>CONNECTED
>>>>>>>>>>
>>>>>>>>>>2)
>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>>>    -> pin_id: 11
>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device id
>>>>>>>>>>    -> device_id: 2
>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 -> state =
>>>>>>>>>>CONNECTED
>>>>>>>>>> (that will in HW disconnect previously connected pin 10, there
>>>>>>>>>>will be
>>>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>This flow is similar for ice, but there are some differences, although
>>>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and recovered
>>>>>>>>>clock pins which are not directly connected to a dpll (connected
>>>>>>>>>through
>>>>>>>>>the MUX pin).
>>>>>>>>>
>>>>>>>>>1)
>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>pin_id: 13
>>>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin -> pin_id: 2
>>>>>>>>>   (in case of dpll_id is needed, would be find in this response also)
>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>>>all the
>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>
>>>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are setting
>>>>>>>>state on DPLL device.
>>>>>>>>
>>>>>>>>
>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to CONNECTED
>>>>>>>>>with
>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>
>>>>>>>>For this you need pin_id and pin_parent_id because you set the state on
>>>>>>>>a parent pin.
>>>>>>>>
>>>>>>>>
>>>>>>>>Yeah, this is exactly why I initially was in favour of hiding all the
>>>>>>>>muxes and magic around it hidden from the user. Now every userspace app
>>>>>>>>working with this has to implement a logic of tracking pin and the mux
>>>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>>>just
>>>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>>>
>>>>>>>>
>>>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>>>
>>>>>>>
>>>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>>>fact
>>>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>>>AUTOMATIC mode.
>>>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>>>type
>>>>>>>pin.
>>>>>>
>>>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>>>work with it.
>>>>>>
>>>>>
>>>>>This is dpll subsystem not SyncE one.
>>>>
>>>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>>>important.
>>>>
>>>
>>>But it is still a dpll subsystem.
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>>Do we need a dpll library to do this magic?
>>>>>>
>>>>>
>>>>>IMHO rather SyncE library :)
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>>
>>>>>>>Thank you!
>>>>>>>Arkadiusz
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>pin_id: 14
>>>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin -> pin_id: 2
>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id: 2) to
>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0, while
>>>>>>>>>all the
>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to CONNECTED
>>>>>>>>>with
>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>>
>>>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d) required
>>>>>>>>>due to
>>>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>>>
>>>>>>>>>Thank you!
>>>>>>>>>Arkadiusz
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Thanks!
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>[...]
>>>>>>>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-25  8:03                       ` Jiri Pirko
@ 2023-07-25 14:01                         ` Kubalewski, Arkadiusz
  2023-07-26  6:38                           ` Jiri Pirko
  0 siblings, 1 reply; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-25 14:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, July 25, 2023 10:04 AM
>
>Mon, Jul 24, 2023 at 05:03:55PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Saturday, July 22, 2023 8:37 AM
>>>
>>>Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 5:46 PM
>>>>>
>>>>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>>>>
>>>>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>wrote:
>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>>>>
>>>>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>>>wrote:
>>>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>>>>
>>>>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>>>>wrote:
>>>>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>>>>
>>>>>>>>>>>[...]
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>+/**
>>>>>>>>>>>>+ * 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
>>>>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>>>>+ *
>>>>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin-flags.
>>>>>>>>>>>>+ *
>>>>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>>>>+ * Return:
>>>>>>>>>>>>+ * * 0 - OK
>>>>>>>>>>>>+ * * negative - error
>>>>>>>>>>>>+ */
>>>>>>>>>>>>+static int
>>>>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>>>>+{
>>>>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>>>>+	int ret;
>>>>>>>>>>>>+
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun
>>>>>>>>>>>here or
>>>>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Because you are probably still thinking the modes are somehow
>>>>>>>>>>connected
>>>>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>>>>The dpll device mode is a state of DPLL before pins are even
>>>>>>>>>>considered.
>>>>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>>>>monitor
>>>>>>>>>>any of the pins.
>>>>>>>>>>
>>>>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>>>>patchset any description about why we need the freerun mode. What
>>>>>>>>>>>is
>>>>>>>>>>>diffrerent between:
>>>>>>>>>>>1) freerun mode
>>>>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>>>>
>>>>>>>>>>The difference:
>>>>>>>>>>Case I:
>>>>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>>>>AUTOMATIC
>>>>>>>>>>2. switch to AUTOMATIC
>>>>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>>>>
>>>>>>>>>>Case II:
>>>>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>>>>
>>>>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>>>>they
>>>>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>>>>monitoring,
>>>>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>>>>longer as
>>>>>>>>>>dpll need to start the process from scratch.
>>>>>>>>>
>>>>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>>>>
>>>>>>>>
>>>>>>>>And we will have the same discussion later.. But implementation is
>>>>>>>>already
>>>>>>>>there.
>>>>>>>
>>>>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>>>>merged, so anything which is blocking us and is totally optional (as
>>>>>>>this freerun mode) is better to be dropped.
>>>>>>>
>>>>>>
>>>>>>It is not blocking anything. Most of it was defined and available for
>>>>>>long time already. Only ice implementing set_mode is a new part.
>>>>>>No clue what is the problem you are implying here.
>>>>>
>>>>>Problem is that I believe you freerun mode should not exist. I believe
>>>>>it is wrong.
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>As said in our previous discussion, without mode_set there is no
>>>>>>>>point to
>>>>>>>>have
>>>>>>>>command DEVICE_SET at all, and there you said that you are ok with
>>>>>>>>having
>>>>>>>>the
>>>>>>>>command as a placeholder, which doesn't make sense, since it is not
>>>>>>>>used.
>>>>>>>
>>>>>>>I don't see any problem in having enum value reserved. But it does not
>>>>>>>need to be there at all. You can add it to the end of the list when
>>>>>>>needed. No problem. This is not an argument.
>>>>>>>
>>>>>>
>>>>>>The argument is that I already implemented and tested, and have the need
>>>>>>for the
>>>>>>existence to set_mode to configure DPLL, which is there to switch the
>>>>>>mode
>>>>>>between AUTOMATIC and FREERUN.
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>Also this is not HW implementation detail but a synchronizer chip
>>>>>>>>feature,
>>>>>>>>once dpll is in FREERUN mode, the measurements like phase offset
>>>>>>>>between
>>>>>>>>the
>>>>>>>>input and dpll's output won't be available.
>>>>>>>>
>>>>>>>>For the user there is a difference..
>>>>>>>>Enabling the FREERUN mode is a reset button on the dpll's state
>>>>>>>>machine,
>>>>>>>>where disconnecting sources is not, as they are still used,
>>>>>>>>monitored and
>>>>>>>>measured.
>>>>>>>
>>>>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>>>>have a state to indicate the state of the state machine (unlocked,
>>>>>>>locked,
>>>>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>>>>
>>>>>>>Please don't mix config and state. I think we untangled this in the
>>>>>>>past
>>>>>>>:/
>>>>>>
>>>>>>I don't mix anything, this is the way dpll works, which means mode of
>>>>>>dpll.
>>>>>
>>>>>You do. You want to force-change the state yet you mangle the mode in.
>>>>>The fact that some specific dpll implemented it as mode does not mean it
>>>>>has to be exposed like that to user. We have to find the right
>>>>>abstraction.
>>>>>
>>>>
>>>>Just to make it clear:
>>>>
>>>>AUTOMATIC:
>>>>- inputs monitored, validated, phase measurements available
>>>>- possible states: unlocked, locked, locked-ho-acq, holdover
>>>>
>>>>FREERUN:
>>>>- inputs not monitored, not validated, no phase measurements available
>>>>- possible states: unlocked
>>>
>>>This is your implementation of DPLL. Others may have it done
>>>differently. But the fact the input is monitored or not, does not make
>>>any difference from user perspective.
>>>
>>>When he has automatic mode and does:
>>>1) disconnect all pins
>>>2) reset state    (however you implement it in the driver is totaly up
>>>		   to the device, you may go to your freerun dpll mode
>>>		   internally and to automatic back, up to you)
>>> -> state will go to unlocked
>>>
>>>The behaviour is exactly the same, without any special mode.
>>
>>In this case there is special reset button, which doesn't exist in
>>reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>>to pretend the some kind of reset has happened, where in reality dpll went
>>to
>>FREERUN and AUTOMATIC.
>
>There are 3 pin states:
>disconnected
>connected
>selectable
>
>When the last source disconnects, go to your internal freerun.
>When some source gets selectable or connected, go to your internal
>automatic mode.
>

This would make the driver to check if all the sources are disconnected
each time someone disconnects a source. Which in first place is not
efficient, but also dpll design already allows different driver instances to
control separated sources, which in this case would force a driver to implement
additional communication between the instances just to allow such hidden
FREERUN mode.
Which seems another argument not to do this in the way you are proposing:
inefficient and unnecessarily complicated.

We know that you could also implement FREERUN mode by disconnecting all the
sources, even if HW doesn't support it explicitly.

>From user perspactive, the mode didn't change.
>

The user didn't change the mode, the mode shall not change.
You wrote to do it silently, so user didn't change the mode but it would have
changed, and we would have pretended the different working mode of DPLL doesn't
exist.

>From user perepective, this is exacly the behaviour he requested.
>

IMHO this is wrong and comes from the definition of pin state DISCONNECTED,
which is not sharp, for our HW means that the input will not be considered
as valid input, but is not disconnecting anything, as input is still
monitored and measured.
Shall we have additional mode like PIN_STATE_NOT_SELECTABLE? As it is not
possible to actually disconnect a pin..

>
>>For me it seems it seems like unnecessary complication of user's life.
>>The idea of FREERUN mode is to run dpll on its system clock, so all the
>>"external" dpll sources shall be disconnected when dpll is in FREERUN.
>
>Yes, that is when you set all pins to disconnect. no mode change needed.
>

We don't disconnect anything, we used a pin state DISCONNECTED as this seemed
most appropriate.

>
>>Let's assume your HW doesn't have a FREERUN, can't you just create it by
>>disconnecting all the sources?
>
>Yep, that's what we do.
>

No, you were saying that the mode doesn't exist and that your hardware doesn't
support it. At the same time it can be achieved by manually disconnecting all
the sources.

>
>>BTW, what chip are you using on mlx5 for this?
>>I don't understand why the user would have to mangle state of all the pins
>>just
>>to stop dpll's work if he could just go into FREERUN and voila. Also what if
>>user doesn't want change the configuration of the pins at all, and he just
>>want
>>to desynchronize it's dpll for i.e. testing reason.
>
>I tried to explain multiple times. Let the user have clean an abstracted
>api, with clear semantics. Simple as that. Your internal freerun mode is
>just something to abstract out, it is not needed to expose it.
>

Our hardware can support in total 4 modes, and 2 are now supported in ice.
I don't get the idea for abstraction of hardware switches, modes or
capabilities, and having those somehow achievable through different
functionalities.

I think we already discussed this long enough to make a decision..
Though I am not convinced by your arguments, and you are not convinced by mine.

Perhaps someone else could step in and cut the rope, so we could go further
with this?

Thank you!
Arkadiusz


>
>>
>>>
>>>We are talking about UAPI here. It should provide the abstraction, leaving
>>>the
>>>internal implementation behind the curtain. What is important is:
>>>1) clear configuration knobs
>>>2) the outcome (hw behaviour)
>>>
>>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>>>>to hit this button.
>>>>>>>
>>>>>>
>>>>>>As already said there are measurement in place in AUTOMATIC, there are
>>>>>>no
>>>>>>such
>>>>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>>>>which
>>>>>>is a side effect of going to FREERUN.
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>So probably most important fact that you are missing here: assuming the
>>>>>>>>user
>>>>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>>>>UNLOCKED
>>>>>>>>state but into HOLDOVER.
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why?
>>>>>>>>>>>This
>>>>>>>>>>>needs to be documented, please.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>>>>
>>>>>>>>>No, please drop it from this patchset. I have no clue why you
>>>>>>>>>readded
>>>>>>>>>it in the first place in the last patchset version.
>>>>>>>>>
>>>>>>>>
>>>>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>>>>driver
>>>>>>>>as it should.
>>>>>>>
>>>>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>>>>remove it. It can be easily added when needed. No problem.
>>>>>>>
>>>>>>
>>>>>>Well, I don't understand the fixation about removing it.
>>>>>
>>>>>It is needed only for your freerun mode, which is questionable. This
>>>>>discussion it not about mode_set. I don't care about it, if it is
>>>>>needed, should be there, if not, so be it.
>>>>>
>>>>>As you say, you need existance of your freerun mode to justify existence
>>>>>of mode_set(). Could you please, please drop both for now so we can
>>>>>move on? I'm tired of this. Thanks!
>>>>>
>>>>
>>>>Reason for dpll subsystem is to control the dpll. So the mode_set and
>>>>different modes are there for the same reason.
>>>>Explained this multiple times already, we need a way to let the user switch
>>>>to FREERUN, so all the activities on dpll are stopped.
>>>>
>>>>>
>>>>>>set_mode was there for a long time, now the callback is properly
>>>>>>implemented
>>>>>>and you are trying to imply that this is not needed.
>>>>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>>>>to do its work.
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>>>>connected with a single DPLL pin:
>>>>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>>>>
>>>>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>>>>
>>>>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>>>>
>>>>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>For mlx5 it goes like:
>>>>>>>>>>>
>>>>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>>>>1)
>>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>>>>    -> pin_id: 10
>>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>>>>    -> device_id: 2
>>>>>>>>>>
>>>>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it
>>>>>>>>>>doesn't
>>>>>>>>>>relate to the dpll interface..
>>>>>>>>>
>>>>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as
>>>>>>>>>input.
>>>>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>>>>
>>>>>>>>
>>>>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon
>>>>>>>>shall
>>>>>>>>know already something about the dpll it is managing.
>>>>>>>>Not saying it is not needed, I am saying this is not a moment the
>>>>>>>>SyncE
>>>>>>>>daemon
>>>>>>>>learns it.
>>>>>>>
>>>>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>>>>
>>>>>>>
>>>>>>>>But let's park it, as this is not really relevant.
>>>>>>>
>>>>>>>Agreed.
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 ->
>>>>>>>>>>>state =
>>>>>>>>>>>CONNECTED
>>>>>>>>>>>
>>>>>>>>>>>2)
>>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>>>>    -> pin_id: 11
>>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device
>>>>>>>>>>>id
>>>>>>>>>>>    -> device_id: 2
>>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 ->
>>>>>>>>>>>state =
>>>>>>>>>>>CONNECTED
>>>>>>>>>>> (that will in HW disconnect previously connected pin 10, there
>>>>>>>>>>>will be
>>>>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>This flow is similar for ice, but there are some differences,
>>>>>>>>>>although
>>>>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and
>>>>>>>>>>recovered
>>>>>>>>>>clock pins which are not directly connected to a dpll (connected
>>>>>>>>>>through
>>>>>>>>>>the MUX pin).
>>>>>>>>>>
>>>>>>>>>>1)
>>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>>pin_id: 13
>>>>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin ->
>>>>>>>>>>pin_id: 2
>>>>>>>>>>   (in case of dpll_id is needed, would be find in this response
>>>>>>>>>>also)
>>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id:
>>>>>>>>>>2) to
>>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0,
>>>>>>>>>>while
>>>>>>>>>>all the
>>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>>
>>>>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are
>>>>>>>>>setting
>>>>>>>>>state on DPLL device.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to
>>>>>>>>>>CONNECTED
>>>>>>>>>>with
>>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>>
>>>>>>>>>For this you need pin_id and pin_parent_id because you set the
>>>>>>>>>state on
>>>>>>>>>a parent pin.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Yeah, this is exactly why I initially was in favour of hiding all
>>>>>>>>>the
>>>>>>>>>muxes and magic around it hidden from the user. Now every userspace
>>>>>>>>>app
>>>>>>>>>working with this has to implement a logic of tracking pin and the
>>>>>>>>>mux
>>>>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>>>>just
>>>>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>>>>
>>>>>>>>
>>>>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>>>>fact
>>>>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>>>>AUTOMATIC mode.
>>>>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>>>>type
>>>>>>>>pin.
>>>>>>>
>>>>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>>>>work with it.
>>>>>>>
>>>>>>
>>>>>>This is dpll subsystem not SyncE one.
>>>>>
>>>>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>>>>important.
>>>>>
>>>>
>>>>But it is still a dpll subsystem.
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>>
>>>>>>>Do we need a dpll library to do this magic?
>>>>>>>
>>>>>>
>>>>>>IMHO rather SyncE library :)
>>>>>>
>>>>>>Thank you!
>>>>>>Arkadiusz
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>Thank you!
>>>>>>>>Arkadiusz
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>>pin_id: 14
>>>>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin ->
>>>>>>>>>>pin_id: 2
>>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id:
>>>>>>>>>>2) to
>>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0,
>>>>>>>>>>while
>>>>>>>>>>all the
>>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to
>>>>>>>>>>CONNECTED
>>>>>>>>>>with
>>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>>>
>>>>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d)
>>>>>>>>>>required
>>>>>>>>>>due to
>>>>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>>>>
>>>>>>>>>>Thank you!
>>>>>>>>>>Arkadiusz
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Thanks!
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>[...]
>>>>>>>>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 12:02             ` Jiri Pirko
  (?)
  (?)
@ 2023-07-25 22:49             ` Jakub Kicinski
  2023-07-26 15:20               ` Paolo Abeni
                                 ` (2 more replies)
  -1 siblings, 3 replies; 109+ messages in thread
From: Jakub Kicinski @ 2023-07-25 22:49 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
> So it is not a mode! Mode is either "automatic" or "manual". Then we
> have a state to indicate the state of the state machine (unlocked, locked,
> holdover, holdover-acq). So what you seek is a way for the user to
> expliticly set the state to "unlocked" and reset of the state machine.

+1 for mixing the state machine and config.
Maybe a compromise would be to rename the config mode?
Detached? Standalone?

> Please don't mix config and state. I think we untangled this in the past
> :/
> 
> Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
> to hit this button.

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-25 14:01                         ` Kubalewski, Arkadiusz
@ 2023-07-26  6:38                           ` Jiri Pirko
  2023-07-26 21:11                             ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 109+ messages in thread
From: Jiri Pirko @ 2023-07-26  6:38 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

Tue, Jul 25, 2023 at 04:01:33PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, July 25, 2023 10:04 AM
>>
>>Mon, Jul 24, 2023 at 05:03:55PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Saturday, July 22, 2023 8:37 AM
>>>>
>>>>Fri, Jul 21, 2023 at 09:48:18PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 5:46 PM
>>>>>>
>>>>>>Fri, Jul 21, 2023 at 03:36:17PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>wrote:
>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>Sent: Friday, July 21, 2023 2:02 PM
>>>>>>>>
>>>>>>>>Fri, Jul 21, 2023 at 01:17:59PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>>wrote:
>>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>>Sent: Friday, July 21, 2023 9:33 AM
>>>>>>>>>>
>>>>>>>>>>Thu, Jul 20, 2023 at 07:31:14PM CEST, arkadiusz.kubalewski@intel.com
>>>>>>>>>>wrote:
>>>>>>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>>>>>>Sent: Thursday, July 20, 2023 4:09 PM
>>>>>>>>>>>>
>>>>>>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>>>>>>wrote:
>>>>>>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>>>>>>>
>>>>>>>>>>>>[...]
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>>+/**
>>>>>>>>>>>>>+ * 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
>>>>>>>>>>>>>+ * @extack: error reporting
>>>>>>>>>>>>>+ *
>>>>>>>>>>>>>+ * Enable a pin on both dplls. Store current state in pin-flags.
>>>>>>>>>>>>>+ *
>>>>>>>>>>>>>+ * Context: Called under pf->dplls.lock
>>>>>>>>>>>>>+ * Return:
>>>>>>>>>>>>>+ * * 0 - OK
>>>>>>>>>>>>>+ * * negative - error
>>>>>>>>>>>>>+ */
>>>>>>>>>>>>>+static int
>>>>>>>>>>>>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>>>>>>>>>>>>+		    enum ice_dpll_pin_type pin_type,
>>>>>>>>>>>>>+		    struct netlink_ext_ack *extack)
>>>>>>>>>>>>>+{
>>>>>>>>>>>>>+	u8 flags = 0;
>>>>>>>>>>>>>+	int ret;
>>>>>>>>>>>>>+
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>I don't follow. Howcome you don't check if the mode is freerun
>>>>>>>>>>>>here or
>>>>>>>>>>>>not? Is it valid to enable a pin when freerun mode? What happens?
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Because you are probably still thinking the modes are somehow
>>>>>>>>>>>connected
>>>>>>>>>>>to the state of the pin, but it is the other way around.
>>>>>>>>>>>The dpll device mode is a state of DPLL before pins are even
>>>>>>>>>>>considered.
>>>>>>>>>>>If the dpll is in mode FREERUN, it shall not try to synchronize or
>>>>>>>>>>>monitor
>>>>>>>>>>>any of the pins.
>>>>>>>>>>>
>>>>>>>>>>>>Also, I am probably slow, but I still don't see anywhere in this
>>>>>>>>>>>>patchset any description about why we need the freerun mode. What
>>>>>>>>>>>>is
>>>>>>>>>>>>diffrerent between:
>>>>>>>>>>>>1) freerun mode
>>>>>>>>>>>>2) automatic mode & all pins disabled?
>>>>>>>>>>>
>>>>>>>>>>>The difference:
>>>>>>>>>>>Case I:
>>>>>>>>>>>1. set dpll to FREERUN and configure the source as if it would be in
>>>>>>>>>>>AUTOMATIC
>>>>>>>>>>>2. switch to AUTOMATIC
>>>>>>>>>>>3. connecting to the valid source takes ~50 seconds
>>>>>>>>>>>
>>>>>>>>>>>Case II:
>>>>>>>>>>>1. set dpll to AUTOMATIC, set all the source to disconnected
>>>>>>>>>>>2. switch one valid source to SELECTABLE
>>>>>>>>>>>3. connecting to the valid source takes ~10 seconds
>>>>>>>>>>>
>>>>>>>>>>>Basically in AUTOMATIC mode the sources are still monitored even when
>>>>>>>>>>>they
>>>>>>>>>>>are not in SELECTABLE state, while in FREERUN there is no such
>>>>>>>>>>>monitoring,
>>>>>>>>>>>so in the end process of synchronizing with the source takes much
>>>>>>>>>>>longer as
>>>>>>>>>>>dpll need to start the process from scratch.
>>>>>>>>>>
>>>>>>>>>>I believe this is implementation detail of your HW. How you do it is up
>>>>>>>>>>to you. User does not have any visibility to this behaviour, therefore
>>>>>>>>>>makes no sense to expose UAPI that is considering it. Please drop it at
>>>>>>>>>>least for the initial patchset version. If you really need it later on
>>>>>>>>>>(which I honestly doubt), you can send it as a follow-up patchset.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>And we will have the same discussion later.. But implementation is
>>>>>>>>>already
>>>>>>>>>there.
>>>>>>>>
>>>>>>>>Yeah, it wouldn't block the initial submission. I would like to see this
>>>>>>>>merged, so anything which is blocking us and is totally optional (as
>>>>>>>>this freerun mode) is better to be dropped.
>>>>>>>>
>>>>>>>
>>>>>>>It is not blocking anything. Most of it was defined and available for
>>>>>>>long time already. Only ice implementing set_mode is a new part.
>>>>>>>No clue what is the problem you are implying here.
>>>>>>
>>>>>>Problem is that I believe you freerun mode should not exist. I believe
>>>>>>it is wrong.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>As said in our previous discussion, without mode_set there is no
>>>>>>>>>point to
>>>>>>>>>have
>>>>>>>>>command DEVICE_SET at all, and there you said that you are ok with
>>>>>>>>>having
>>>>>>>>>the
>>>>>>>>>command as a placeholder, which doesn't make sense, since it is not
>>>>>>>>>used.
>>>>>>>>
>>>>>>>>I don't see any problem in having enum value reserved. But it does not
>>>>>>>>need to be there at all. You can add it to the end of the list when
>>>>>>>>needed. No problem. This is not an argument.
>>>>>>>>
>>>>>>>
>>>>>>>The argument is that I already implemented and tested, and have the need
>>>>>>>for the
>>>>>>>existence to set_mode to configure DPLL, which is there to switch the
>>>>>>>mode
>>>>>>>between AUTOMATIC and FREERUN.
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>Also this is not HW implementation detail but a synchronizer chip
>>>>>>>>>feature,
>>>>>>>>>once dpll is in FREERUN mode, the measurements like phase offset
>>>>>>>>>between
>>>>>>>>>the
>>>>>>>>>input and dpll's output won't be available.
>>>>>>>>>
>>>>>>>>>For the user there is a difference..
>>>>>>>>>Enabling the FREERUN mode is a reset button on the dpll's state
>>>>>>>>>machine,
>>>>>>>>>where disconnecting sources is not, as they are still used,
>>>>>>>>>monitored and
>>>>>>>>>measured.
>>>>>>>>
>>>>>>>>So it is not a mode! Mode is either "automatic" or "manual". Then we
>>>>>>>>have a state to indicate the state of the state machine (unlocked,
>>>>>>>>locked,
>>>>>>>>holdover, holdover-acq). So what you seek is a way for the user to
>>>>>>>>expliticly set the state to "unlocked" and reset of the state machine.
>>>>>>>>
>>>>>>>>Please don't mix config and state. I think we untangled this in the
>>>>>>>>past
>>>>>>>>:/
>>>>>>>
>>>>>>>I don't mix anything, this is the way dpll works, which means mode of
>>>>>>>dpll.
>>>>>>
>>>>>>You do. You want to force-change the state yet you mangle the mode in.
>>>>>>The fact that some specific dpll implemented it as mode does not mean it
>>>>>>has to be exposed like that to user. We have to find the right
>>>>>>abstraction.
>>>>>>
>>>>>
>>>>>Just to make it clear:
>>>>>
>>>>>AUTOMATIC:
>>>>>- inputs monitored, validated, phase measurements available
>>>>>- possible states: unlocked, locked, locked-ho-acq, holdover
>>>>>
>>>>>FREERUN:
>>>>>- inputs not monitored, not validated, no phase measurements available
>>>>>- possible states: unlocked
>>>>
>>>>This is your implementation of DPLL. Others may have it done
>>>>differently. But the fact the input is monitored or not, does not make
>>>>any difference from user perspective.
>>>>
>>>>When he has automatic mode and does:
>>>>1) disconnect all pins
>>>>2) reset state    (however you implement it in the driver is totaly up
>>>>		   to the device, you may go to your freerun dpll mode
>>>>		   internally and to automatic back, up to you)
>>>> -> state will go to unlocked
>>>>
>>>>The behaviour is exactly the same, without any special mode.
>>>
>>>In this case there is special reset button, which doesn't exist in
>>>reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>>>to pretend the some kind of reset has happened, where in reality dpll went
>>>to
>>>FREERUN and AUTOMATIC.
>>
>>There are 3 pin states:
>>disconnected
>>connected
>>selectable
>>
>>When the last source disconnects, go to your internal freerun.
>>When some source gets selectable or connected, go to your internal
>>automatic mode.
>>
>
>This would make the driver to check if all the sources are disconnected
>each time someone disconnects a source. Which in first place is not
>efficient, but also dpll design already allows different driver instances to
>control separated sources, which in this case would force a driver to implement
>additional communication between the instances just to allow such hidden
>FREERUN mode.
>Which seems another argument not to do this in the way you are proposing:
>inefficient and unnecessarily complicated.
>
>We know that you could also implement FREERUN mode by disconnecting all the
>sources, even if HW doesn't support it explicitly.
>
>>From user perspactive, the mode didn't change.
>>
>
>The user didn't change the mode, the mode shall not change.
>You wrote to do it silently, so user didn't change the mode but it would have
>changed, and we would have pretended the different working mode of DPLL doesn't
>exist.
>
>>From user perepective, this is exacly the behaviour he requested.
>>
>
>IMHO this is wrong and comes from the definition of pin state DISCONNECTED,
>which is not sharp, for our HW means that the input will not be considered
>as valid input, but is not disconnecting anything, as input is still
>monitored and measured.
>Shall we have additional mode like PIN_STATE_NOT_SELECTABLE? As it is not
>possible to actually disconnect a pin..
>
>>
>>>For me it seems it seems like unnecessary complication of user's life.
>>>The idea of FREERUN mode is to run dpll on its system clock, so all the
>>>"external" dpll sources shall be disconnected when dpll is in FREERUN.
>>
>>Yes, that is when you set all pins to disconnect. no mode change needed.
>>
>
>We don't disconnect anything, we used a pin state DISCONNECTED as this seemed
>most appropriate.
>
>>
>>>Let's assume your HW doesn't have a FREERUN, can't you just create it by
>>>disconnecting all the sources?
>>
>>Yep, that's what we do.
>>
>
>No, you were saying that the mode doesn't exist and that your hardware doesn't
>support it. At the same time it can be achieved by manually disconnecting all
>the sources.
>
>>
>>>BTW, what chip are you using on mlx5 for this?
>>>I don't understand why the user would have to mangle state of all the pins
>>>just
>>>to stop dpll's work if he could just go into FREERUN and voila. Also what if
>>>user doesn't want change the configuration of the pins at all, and he just
>>>want
>>>to desynchronize it's dpll for i.e. testing reason.
>>
>>I tried to explain multiple times. Let the user have clean an abstracted
>>api, with clear semantics. Simple as that. Your internal freerun mode is
>>just something to abstract out, it is not needed to expose it.
>>
>
>Our hardware can support in total 4 modes, and 2 are now supported in ice.
>I don't get the idea for abstraction of hardware switches, modes or
>capabilities, and having those somehow achievable through different
>functionalities.
>
>I think we already discussed this long enough to make a decision..
>Though I am not convinced by your arguments, and you are not convinced by mine.
>
>Perhaps someone else could step in and cut the rope, so we could go further
>with this?

Or, even better, please drop this for the initial patchset and have this
as a follow-up. Thanks!


>
>Thank you!
>Arkadiusz
>
>
>>
>>>
>>>>
>>>>We are talking about UAPI here. It should provide the abstraction, leaving
>>>>the
>>>>internal implementation behind the curtain. What is important is:
>>>>1) clear configuration knobs
>>>>2) the outcome (hw behaviour)
>>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>>>>>>>>to hit this button.
>>>>>>>>
>>>>>>>
>>>>>>>As already said there are measurement in place in AUTOMATIC, there are
>>>>>>>no
>>>>>>>such
>>>>>>>thing in FREERUN. Going into FREERUN resets the state machine of dpll
>>>>>>>which
>>>>>>>is a side effect of going to FREERUN.
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>So probably most important fact that you are missing here: assuming the
>>>>>>>>>user
>>>>>>>>>disconnects the pin that dpll was locked with, our dpll doesn't go into
>>>>>>>>>UNLOCKED
>>>>>>>>>state but into HOLDOVER.
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>Isn't the behaviour of 1) and 2) exactly the same? If no, why?
>>>>>>>>>>>>This
>>>>>>>>>>>>needs to be documented, please.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Sure will add the description of FREERUN to the docs.
>>>>>>>>>>
>>>>>>>>>>No, please drop it from this patchset. I have no clue why you
>>>>>>>>>>readded
>>>>>>>>>>it in the first place in the last patchset version.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>mode_set was there from the very beginning.. now implemented in ice
>>>>>>>>>driver
>>>>>>>>>as it should.
>>>>>>>>
>>>>>>>>I don't understand the fixation on a callback to be implemented. Just
>>>>>>>>remove it. It can be easily added when needed. No problem.
>>>>>>>>
>>>>>>>
>>>>>>>Well, I don't understand the fixation about removing it.
>>>>>>
>>>>>>It is needed only for your freerun mode, which is questionable. This
>>>>>>discussion it not about mode_set. I don't care about it, if it is
>>>>>>needed, should be there, if not, so be it.
>>>>>>
>>>>>>As you say, you need existance of your freerun mode to justify existence
>>>>>>of mode_set(). Could you please, please drop both for now so we can
>>>>>>move on? I'm tired of this. Thanks!
>>>>>>
>>>>>
>>>>>Reason for dpll subsystem is to control the dpll. So the mode_set and
>>>>>different modes are there for the same reason.
>>>>>Explained this multiple times already, we need a way to let the user switch
>>>>>to FREERUN, so all the activities on dpll are stopped.
>>>>>
>>>>>>
>>>>>>>set_mode was there for a long time, now the callback is properly
>>>>>>>implemented
>>>>>>>and you are trying to imply that this is not needed.
>>>>>>>We require it, as there is no other other way to stop AUTOMATIC mode dpll
>>>>>>>to do its work.
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>Another question, I asked the last time as well, but was not heard:
>>>>>>>>>>>>Consider example where you have 2 netdevices, eth0 and eth1, each
>>>>>>>>>>>>connected with a single DPLL pin:
>>>>>>>>>>>>eth0 - DPLL pin 10 (DPLL device id 2)
>>>>>>>>>>>>eth1 - DPLL pin 11 (DPLL device id 2)
>>>>>>>>>>>>
>>>>>>>>>>>>You have a SyncE daemon running on top eth0 and eth1.
>>>>>>>>>>>>
>>>>>>>>>>>>Could you please describe following 2 flows?
>>>>>>>>>>>>
>>>>>>>>>>>>1) SyncE daemon selects eth0 as a source of clock
>>>>>>>>>>>>2) SyncE daemon selects eth1 as a source of clock
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>For mlx5 it goes like:
>>>>>>>>>>>>
>>>>>>>>>>>>DPLL device mode is MANUAL.
>>>>>>>>>>>>1)
>>>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth0
>>>>>>>>>>>>    -> pin_id: 10
>>>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 10 to get DPLL device id
>>>>>>>>>>>>    -> device_id: 2
>>>>>>>>>>>
>>>>>>>>>>>Not sure if it needs to obtain the dpll id in this step, but it
>>>>>>>>>>>doesn't
>>>>>>>>>>>relate to the dpll interface..
>>>>>>>>>>
>>>>>>>>>>Sure it has to. The PIN_SET accepts pin_id and device_id attrs as
>>>>>>>>>>input.
>>>>>>>>>>You need to set the state on a pin on a certain DPLL device.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>The thing is pin can be connected to multiple dplls and SyncE daemon
>>>>>>>>>shall
>>>>>>>>>know already something about the dpll it is managing.
>>>>>>>>>Not saying it is not needed, I am saying this is not a moment the
>>>>>>>>>SyncE
>>>>>>>>>daemon
>>>>>>>>>learns it.
>>>>>>>>
>>>>>>>>Moment or not, it is needed for the cmd, that is why I have it there.
>>>>>>>>
>>>>>>>>
>>>>>>>>>But let's park it, as this is not really relevant.
>>>>>>>>
>>>>>>>>Agreed.
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 ->
>>>>>>>>>>>>state =
>>>>>>>>>>>>CONNECTED
>>>>>>>>>>>>
>>>>>>>>>>>>2)
>>>>>>>>>>>> SynceE daemon uses RTNetlink to obtain DPLL pin number of eth1
>>>>>>>>>>>>    -> pin_id: 11
>>>>>>>>>>>> SenceE daemon will use PIN_GET with pin_id 11 to get DPLL device
>>>>>>>>>>>>id
>>>>>>>>>>>>    -> device_id: 2
>>>>>>>>>>>> SynceE daemon does PIN_SET cmd on pin_id 10, device_id 2 ->
>>>>>>>>>>>>state =
>>>>>>>>>>>>CONNECTED
>>>>>>>>>>>> (that will in HW disconnect previously connected pin 10, there
>>>>>>>>>>>>will be
>>>>>>>>>>>>  notification of pin_id 10, device_id -> state DISCONNECT)
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>This flow is similar for ice, but there are some differences,
>>>>>>>>>>>although
>>>>>>>>>>>they come from the fact, the ice is using AUTOMATIC mode and
>>>>>>>>>>>recovered
>>>>>>>>>>>clock pins which are not directly connected to a dpll (connected
>>>>>>>>>>>through
>>>>>>>>>>>the MUX pin).
>>>>>>>>>>>
>>>>>>>>>>>1)
>>>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>>>pin_id: 13
>>>>>>>>>>>b) SyncE daemon uses PIN_GET to find a parent MUX type pin ->
>>>>>>>>>>>pin_id: 2
>>>>>>>>>>>   (in case of dpll_id is needed, would be find in this response
>>>>>>>>>>>also)
>>>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id:
>>>>>>>>>>>2) to
>>>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0,
>>>>>>>>>>>while
>>>>>>>>>>>all the
>>>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>>>
>>>>>>>>>>Yeah, for this you need pin_id 2 and device_id. Because you are
>>>>>>>>>>setting
>>>>>>>>>>state on DPLL device.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:13 to
>>>>>>>>>>>CONNECTED
>>>>>>>>>>>with
>>>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>>>
>>>>>>>>>>For this you need pin_id and pin_parent_id because you set the
>>>>>>>>>>state on
>>>>>>>>>>a parent pin.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Yeah, this is exactly why I initially was in favour of hiding all
>>>>>>>>>>the
>>>>>>>>>>muxes and magic around it hidden from the user. Now every userspace
>>>>>>>>>>app
>>>>>>>>>>working with this has to implement a logic of tracking pin and the
>>>>>>>>>>mux
>>>>>>>>>>parents (possibly multiple levels) and configure everything. But it
>>>>>>>>>>just
>>>>>>>>>>need a simple thing: "select this pin as a source" :/
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>Jakub, isn't this sort of unnecessary HW-details complexicity exposure
>>>>>>>>>>in UAPI you were against in the past? Am I missing something?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>Multiple level of muxes possibly could be hidden in the driver, but the
>>>>>>>>>fact
>>>>>>>>>they exist is not possible to be hidden from the user if the DPLL is in
>>>>>>>>>AUTOMATIC mode.
>>>>>>>>>For MANUAL mode dpll the muxes could be also hidden.
>>>>>>>>>Yeah, we have in ice most complicated scenario of AUTOMATIC mode + MUXED
>>>>>>>>>type
>>>>>>>>>pin.
>>>>>>>>
>>>>>>>>Sure, but does user care how complicated things are inside? The syncE
>>>>>>>>daemon just cares for: "select netdev x as a source". However it is done
>>>>>>>>internally is irrelevant to him. With the existing UAPI, the syncE
>>>>>>>>daemon needs to learn individual device dpll/pin/mux topology and
>>>>>>>>work with it.
>>>>>>>>
>>>>>>>
>>>>>>>This is dpll subsystem not SyncE one.
>>>>>>
>>>>>>SyncE is very legit use case of the UAPI. I would say perhaps the most
>>>>>>important.
>>>>>>
>>>>>
>>>>>But it is still a dpll subsystem.
>>>>>
>>>>>Thank you!
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>Do we need a dpll library to do this magic?
>>>>>>>>
>>>>>>>
>>>>>>>IMHO rather SyncE library :)
>>>>>>>
>>>>>>>Thank you!
>>>>>>>Arkadiusz
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>Thank you!
>>>>>>>>>Arkadiusz
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>2) (basically the same, only eth1 would get different pin_id.)
>>>>>>>>>>>a) SyncE daemon uses RTNetlink to obtain DPLL pin number of eth0 ->
>>>>>>>>>>>pin_id: 14
>>>>>>>>>>>b) SyncE daemon uses PIN_GET to find parent MUX type pin ->
>>>>>>>>>>>pin_id: 2
>>>>>>>>>>>c) SyncE daemon uses PIN_SET to set parent MUX type pin (pin_id:
>>>>>>>>>>>2) to
>>>>>>>>>>>   pin-state: SELECTABLE and highest priority (i.e. pin-prio:0,
>>>>>>>>>>>while
>>>>>>>>>>>all the
>>>>>>>>>>>   other pins shall be lower prio i.e. pin-prio:1)
>>>>>>>>>>>d) SyncE daemon uses PIN_SET to set state of pin_id:14 to
>>>>>>>>>>>CONNECTED
>>>>>>>>>>>with
>>>>>>>>>>>   parent pin (pin-id:2)
>>>>>>>>>>>
>>>>>>>>>>>Where step c) is required due to AUTOMATIC mode, and step d)
>>>>>>>>>>>required
>>>>>>>>>>>due to
>>>>>>>>>>>phy recovery clock pin being connected through the MUX type pin.
>>>>>>>>>>>
>>>>>>>>>>>Thank you!
>>>>>>>>>>>Arkadiusz
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>Thanks!
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>[...]
>>>>>>>>>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-25 22:49             ` Jakub Kicinski
@ 2023-07-26 15:20               ` Paolo Abeni
  2023-07-26 21:10                 ` Kubalewski, Arkadiusz
  2023-07-31 12:08                   ` Jiri Pirko
  2023-07-26 21:08               ` Kubalewski, Arkadiusz
  2023-07-31 12:11                 ` Jiri Pirko
  2 siblings, 2 replies; 109+ messages in thread
From: Paolo Abeni @ 2023-07-26 15:20 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

On Tue, 2023-07-25 at 15:49 -0700, Jakub Kicinski wrote:
> On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
> > So it is not a mode! Mode is either "automatic" or "manual". Then we
> > have a state to indicate the state of the state machine (unlocked, locked,
> > holdover, holdover-acq). So what you seek is a way for the user to
> > expliticly set the state to "unlocked" and reset of the state machine.
> 
> +1 for mixing the state machine and config.
> Maybe a compromise would be to rename the config mode?
> Detached? Standalone?

For the records, I don't know the H/W details to any extents, but
generally speaking it sounds reasonable to me that a mode change could
cause a state change.

e.g. switching an ethernet device autoneg mode could cause the link
state to flip.

So I'm ok with the existence of the freerun mode.

I think it should be clarified what happens if pins are manually
enabled in such mode. I expect ~nothing will change, but stating it
clearly would help.

Cheers,

Paolo


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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-25 22:49             ` Jakub Kicinski
  2023-07-26 15:20               ` Paolo Abeni
@ 2023-07-26 21:08               ` Kubalewski, Arkadiusz
  2023-07-31 12:11                 ` Jiri Pirko
  2 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-26 21:08 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Wednesday, July 26, 2023 12:50 AM
>
>On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> So it is not a mode! Mode is either "automatic" or "manual". Then we
>> have a state to indicate the state of the state machine (unlocked,
>>locked,
>> holdover, holdover-acq). So what you seek is a way for the user to
>> expliticly set the state to "unlocked" and reset of the state machine.
>
>+1 for mixing the state machine and config.
>Maybe a compromise would be to rename the config mode?
>Detached? Standalone?
>

Well, those seems good although standalone a bit like the property of a device.
I am biased by the FREERUN from chip docs and don't have strong opinion
on any of those..

Thank you!
Arkadiusz

>> Please don't mix config and state. I think we untangled this in the past
>> :/
>>
>> Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>> to hit this button.

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-26 15:20               ` Paolo Abeni
@ 2023-07-26 21:10                 ` Kubalewski, Arkadiusz
  2023-07-31 12:08                   ` Jiri Pirko
  1 sibling, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-26 21:10 UTC (permalink / raw)
  To: Paolo Abeni, Jakub Kicinski, Jiri Pirko
  Cc: Vadim Fedorenko, Jonathan Lemon, Olech, Milena, Michalik, Michal,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche


>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Wednesday, July 26, 2023 5:20 PM
>
>On Tue, 2023-07-25 at 15:49 -0700, Jakub Kicinski wrote:
>> On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> > So it is not a mode! Mode is either "automatic" or "manual". Then we
>> > have a state to indicate the state of the state machine (unlocked,
>> > locked,
>> > holdover, holdover-acq). So what you seek is a way for the user to
>> > expliticly set the state to "unlocked" and reset of the state machine.
>>
>> +1 for mixing the state machine and config.
>> Maybe a compromise would be to rename the config mode?
>> Detached? Standalone?
>
>For the records, I don't know the H/W details to any extents, but
>generally speaking it sounds reasonable to me that a mode change could
>cause a state change.
>
>e.g. switching an ethernet device autoneg mode could cause the link
>state to flip.
>
>So I'm ok with the existence of the freerun mode.
>
>I think it should be clarified what happens if pins are manually
>enabled in such mode. I expect ~nothing will change, but stating it
>clearly would help.
>
>Cheers,
>
>Paolo
>

Thank you for the insights!
Arkadiusz

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-26  6:38                           ` Jiri Pirko
@ 2023-07-26 21:11                             ` Kubalewski, Arkadiusz
  2023-07-27 10:28                                 ` Vadim Fedorenko
  0 siblings, 1 reply; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-26 21:11 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: kuba, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, July 26, 2023 8:38 AM
>

[...]
 
>>>>>>
>>>>>>Just to make it clear:
>>>>>>
>>>>>>AUTOMATIC:
>>>>>>- inputs monitored, validated, phase measurements available
>>>>>>- possible states: unlocked, locked, locked-ho-acq, holdover
>>>>>>
>>>>>>FREERUN:
>>>>>>- inputs not monitored, not validated, no phase measurements available
>>>>>>- possible states: unlocked
>>>>>
>>>>>This is your implementation of DPLL. Others may have it done
>>>>>differently. But the fact the input is monitored or not, does not make
>>>>>any difference from user perspective.
>>>>>
>>>>>When he has automatic mode and does:
>>>>>1) disconnect all pins
>>>>>2) reset state    (however you implement it in the driver is totaly up
>>>>>		   to the device, you may go to your freerun dpll mode
>>>>>		   internally and to automatic back, up to you)
>>>>> -> state will go to unlocked
>>>>>
>>>>>The behaviour is exactly the same, without any special mode.
>>>>
>>>>In this case there is special reset button, which doesn't exist in
>>>>reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>>>>to pretend the some kind of reset has happened, where in reality dpll went
>>>>to
>>>>FREERUN and AUTOMATIC.
>>>
>>>There are 3 pin states:
>>>disconnected
>>>connected
>>>selectable
>>>
>>>When the last source disconnects, go to your internal freerun.
>>>When some source gets selectable or connected, go to your internal
>>>automatic mode.
>>>
>>
>>This would make the driver to check if all the sources are disconnected
>>each time someone disconnects a source. Which in first place is not
>>efficient, but also dpll design already allows different driver instances
>>to
>>control separated sources, which in this case would force a driver to
>>implement
>>additional communication between the instances just to allow such hidden
>>FREERUN mode.
>>Which seems another argument not to do this in the way you are proposing:
>>inefficient and unnecessarily complicated.
>>
>>We know that you could also implement FREERUN mode by disconnecting all
>>the
>>sources, even if HW doesn't support it explicitly.
>>
>>>From user perspactive, the mode didn't change.
>>>
>>
>>The user didn't change the mode, the mode shall not change.
>>You wrote to do it silently, so user didn't change the mode but it would
>have
>>changed, and we would have pretended the different working mode of DPLL
>doesn't
>>exist.
>>
>>>From user perepective, this is exacly the behaviour he requested.
>>>
>>
>>IMHO this is wrong and comes from the definition of pin state DISCONNECTED,
>>which is not sharp, for our HW means that the input will not be considered
>>as valid input, but is not disconnecting anything, as input is still
>>monitored and measured.
>>Shall we have additional mode like PIN_STATE_NOT_SELECTABLE? As it is not
>>possible to actually disconnect a pin..
>>
>>>
>>>>For me it seems it seems like unnecessary complication of user's life.
>>>>The idea of FREERUN mode is to run dpll on its system clock, so all the
>>>>"external" dpll sources shall be disconnected when dpll is in FREERUN.
>>>
>>>Yes, that is when you set all pins to disconnect. no mode change needed.
>>>
>>
>>We don't disconnect anything, we used a pin state DISCONNECTED as this
>>seemed
>>most appropriate.
>>
>>>
>>>>Let's assume your HW doesn't have a FREERUN, can't you just create it by
>>>>disconnecting all the sources?
>>>
>>>Yep, that's what we do.
>>>
>>
>>No, you were saying that the mode doesn't exist and that your hardware
>>doesn't
>>support it. At the same time it can be achieved by manually disconnecting
>>all
>>the sources.
>>
>>>
>>>>BTW, what chip are you using on mlx5 for this?
>>>>I don't understand why the user would have to mangle state of all the pins
>>>>just
>>>>to stop dpll's work if he could just go into FREERUN and voila. Also what
>>>>if
>>>>user doesn't want change the configuration of the pins at all, and he just
>>>>want
>>>>to desynchronize it's dpll for i.e. testing reason.
>>>
>>>I tried to explain multiple times. Let the user have clean an abstracted
>>>api, with clear semantics. Simple as that. Your internal freerun mode is
>>>just something to abstract out, it is not needed to expose it.
>>>
>>
>>Our hardware can support in total 4 modes, and 2 are now supported in ice.
>>I don't get the idea for abstraction of hardware switches, modes or
>>capabilities, and having those somehow achievable through different
>>functionalities.
>>
>>I think we already discussed this long enough to make a decision..
>>Though I am not convinced by your arguments, and you are not convinced by
>>mine.
>>
>>Perhaps someone else could step in and cut the rope, so we could go further
>>with this?
>
>Or, even better, please drop this for the initial patchset and have this
>as a follow-up. Thanks!
>
>

On the responses from Jakub and Paolo, they supported the idea of having
such mode.

Although Jakub have asked if there could be better name then FREERUN, also
suggested DETACHED and STANDALONE.
For me DETACHED seems pretty good, STANDALONE a bit too far..
I am biased by the FREERUN from chip docs and don't have strong opinion
on any of those..

Any suggestions?

Thank you!
Arkadiusz

[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-26 21:11                             ` Kubalewski, Arkadiusz
@ 2023-07-27 10:28                                 ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-27 10:28 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko, Jakub Kicinski, Paolo Abeni
  Cc: Jonathan Lemon, Olech, Milena, Michalik, Michal,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

On 26/07/2023 22:11, Kubalewski, Arkadiusz wrote:
>> From: Jiri Pirko <jiri@resnulli.us>
>> Sent: Wednesday, July 26, 2023 8:38 AM
>>
> 
> [...]
>   
>>>>>>>
>>>>>>> Just to make it clear:
>>>>>>>
>>>>>>> AUTOMATIC:
>>>>>>> - inputs monitored, validated, phase measurements available
>>>>>>> - possible states: unlocked, locked, locked-ho-acq, holdover
>>>>>>>
>>>>>>> FREERUN:
>>>>>>> - inputs not monitored, not validated, no phase measurements available
>>>>>>> - possible states: unlocked
>>>>>>
>>>>>> This is your implementation of DPLL. Others may have it done
>>>>>> differently. But the fact the input is monitored or not, does not make
>>>>>> any difference from user perspective.
>>>>>>
>>>>>> When he has automatic mode and does:
>>>>>> 1) disconnect all pins
>>>>>> 2) reset state    (however you implement it in the driver is totaly up
>>>>>> 		   to the device, you may go to your freerun dpll mode
>>>>>> 		   internally and to automatic back, up to you)
>>>>>> -> state will go to unlocked
>>>>>>
>>>>>> The behaviour is exactly the same, without any special mode.
>>>>>
>>>>> In this case there is special reset button, which doesn't exist in
>>>>> reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>>>>> to pretend the some kind of reset has happened, where in reality dpll went
>>>>> to
>>>>> FREERUN and AUTOMATIC.
>>>>
>>>> There are 3 pin states:
>>>> disconnected
>>>> connected
>>>> selectable
>>>>
>>>> When the last source disconnects, go to your internal freerun.
>>>> When some source gets selectable or connected, go to your internal
>>>> automatic mode.
>>>>
>>>
>>> This would make the driver to check if all the sources are disconnected
>>> each time someone disconnects a source. Which in first place is not
>>> efficient, but also dpll design already allows different driver instances
>>> to
>>> control separated sources, which in this case would force a driver to
>>> implement
>>> additional communication between the instances just to allow such hidden
>>> FREERUN mode.
>>> Which seems another argument not to do this in the way you are proposing:
>>> inefficient and unnecessarily complicated.
>>>
>>> We know that you could also implement FREERUN mode by disconnecting all
>>> the
>>> sources, even if HW doesn't support it explicitly.
>>>
>>> >From user perspactive, the mode didn't change.
>>>>
>>>
>>> The user didn't change the mode, the mode shall not change.
>>> You wrote to do it silently, so user didn't change the mode but it would
>> have
>>> changed, and we would have pretended the different working mode of DPLL
>> doesn't
>>> exist.
>>>
>>> >From user perepective, this is exacly the behaviour he requested.
>>>>
>>>
>>> IMHO this is wrong and comes from the definition of pin state DISCONNECTED,
>>> which is not sharp, for our HW means that the input will not be considered
>>> as valid input, but is not disconnecting anything, as input is still
>>> monitored and measured.
>>> Shall we have additional mode like PIN_STATE_NOT_SELECTABLE? As it is not
>>> possible to actually disconnect a pin..
>>>
>>>>
>>>>> For me it seems it seems like unnecessary complication of user's life.
>>>>> The idea of FREERUN mode is to run dpll on its system clock, so all the
>>>>> "external" dpll sources shall be disconnected when dpll is in FREERUN.
>>>>
>>>> Yes, that is when you set all pins to disconnect. no mode change needed.
>>>>
>>>
>>> We don't disconnect anything, we used a pin state DISCONNECTED as this
>>> seemed
>>> most appropriate.
>>>
>>>>
>>>>> Let's assume your HW doesn't have a FREERUN, can't you just create it by
>>>>> disconnecting all the sources?
>>>>
>>>> Yep, that's what we do.
>>>>
>>>
>>> No, you were saying that the mode doesn't exist and that your hardware
>>> doesn't
>>> support it. At the same time it can be achieved by manually disconnecting
>>> all
>>> the sources.
>>>
>>>>
>>>>> BTW, what chip are you using on mlx5 for this?
>>>>> I don't understand why the user would have to mangle state of all the pins
>>>>> just
>>>>> to stop dpll's work if he could just go into FREERUN and voila. Also what
>>>>> if
>>>>> user doesn't want change the configuration of the pins at all, and he just
>>>>> want
>>>>> to desynchronize it's dpll for i.e. testing reason.
>>>>
>>>> I tried to explain multiple times. Let the user have clean an abstracted
>>>> api, with clear semantics. Simple as that. Your internal freerun mode is
>>>> just something to abstract out, it is not needed to expose it.
>>>>
>>>
>>> Our hardware can support in total 4 modes, and 2 are now supported in ice.
>>> I don't get the idea for abstraction of hardware switches, modes or
>>> capabilities, and having those somehow achievable through different
>>> functionalities.
>>>
>>> I think we already discussed this long enough to make a decision..
>>> Though I am not convinced by your arguments, and you are not convinced by
>>> mine.
>>>
>>> Perhaps someone else could step in and cut the rope, so we could go further
>>> with this?
>>
>> Or, even better, please drop this for the initial patchset and have this
>> as a follow-up. Thanks!
>>
>>
> 
> On the responses from Jakub and Paolo, they supported the idea of having
> such mode.
> 
> Although Jakub have asked if there could be better name then FREERUN, also
> suggested DETACHED and STANDALONE.
> For me DETACHED seems pretty good, STANDALONE a bit too far..
> I am biased by the FREERUN from chip docs and don't have strong opinion
> on any of those..
> 
> Any suggestions?

It looks like we have a kind of split-brain situation, and my thoughts 
are following:
Even though right now we don't have any hardware supporting 
freerun/standalone mode, I do really like the idea to have it. It will 
be used in monitoring implementations where we refer to internal 
oscillator (Rb/Cs) as a source of truth to compare with the signal on 
the other pins. We can name it DETACHED if it sounds better.

> 
> Thank you!
> Arkadiusz
> 
> [...]


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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-27 10:28                                 ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-07-27 10:28 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko, Jakub Kicinski, Paolo Abeni
  Cc: Jonathan Lemon, Olech, Milena, Michalik, Michal,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

On 26/07/2023 22:11, Kubalewski, Arkadiusz wrote:
>> From: Jiri Pirko <jiri@resnulli.us>
>> Sent: Wednesday, July 26, 2023 8:38 AM
>>
> 
> [...]
>   
>>>>>>>
>>>>>>> Just to make it clear:
>>>>>>>
>>>>>>> AUTOMATIC:
>>>>>>> - inputs monitored, validated, phase measurements available
>>>>>>> - possible states: unlocked, locked, locked-ho-acq, holdover
>>>>>>>
>>>>>>> FREERUN:
>>>>>>> - inputs not monitored, not validated, no phase measurements available
>>>>>>> - possible states: unlocked
>>>>>>
>>>>>> This is your implementation of DPLL. Others may have it done
>>>>>> differently. But the fact the input is monitored or not, does not make
>>>>>> any difference from user perspective.
>>>>>>
>>>>>> When he has automatic mode and does:
>>>>>> 1) disconnect all pins
>>>>>> 2) reset state    (however you implement it in the driver is totaly up
>>>>>> 		   to the device, you may go to your freerun dpll mode
>>>>>> 		   internally and to automatic back, up to you)
>>>>>> -> state will go to unlocked
>>>>>>
>>>>>> The behaviour is exactly the same, without any special mode.
>>>>>
>>>>> In this case there is special reset button, which doesn't exist in
>>>>> reality, actually your suggestion to go into FREERUN and back to AUTOMATIC
>>>>> to pretend the some kind of reset has happened, where in reality dpll went
>>>>> to
>>>>> FREERUN and AUTOMATIC.
>>>>
>>>> There are 3 pin states:
>>>> disconnected
>>>> connected
>>>> selectable
>>>>
>>>> When the last source disconnects, go to your internal freerun.
>>>> When some source gets selectable or connected, go to your internal
>>>> automatic mode.
>>>>
>>>
>>> This would make the driver to check if all the sources are disconnected
>>> each time someone disconnects a source. Which in first place is not
>>> efficient, but also dpll design already allows different driver instances
>>> to
>>> control separated sources, which in this case would force a driver to
>>> implement
>>> additional communication between the instances just to allow such hidden
>>> FREERUN mode.
>>> Which seems another argument not to do this in the way you are proposing:
>>> inefficient and unnecessarily complicated.
>>>
>>> We know that you could also implement FREERUN mode by disconnecting all
>>> the
>>> sources, even if HW doesn't support it explicitly.
>>>
>>> >From user perspactive, the mode didn't change.
>>>>
>>>
>>> The user didn't change the mode, the mode shall not change.
>>> You wrote to do it silently, so user didn't change the mode but it would
>> have
>>> changed, and we would have pretended the different working mode of DPLL
>> doesn't
>>> exist.
>>>
>>> >From user perepective, this is exacly the behaviour he requested.
>>>>
>>>
>>> IMHO this is wrong and comes from the definition of pin state DISCONNECTED,
>>> which is not sharp, for our HW means that the input will not be considered
>>> as valid input, but is not disconnecting anything, as input is still
>>> monitored and measured.
>>> Shall we have additional mode like PIN_STATE_NOT_SELECTABLE? As it is not
>>> possible to actually disconnect a pin..
>>>
>>>>
>>>>> For me it seems it seems like unnecessary complication of user's life.
>>>>> The idea of FREERUN mode is to run dpll on its system clock, so all the
>>>>> "external" dpll sources shall be disconnected when dpll is in FREERUN.
>>>>
>>>> Yes, that is when you set all pins to disconnect. no mode change needed.
>>>>
>>>
>>> We don't disconnect anything, we used a pin state DISCONNECTED as this
>>> seemed
>>> most appropriate.
>>>
>>>>
>>>>> Let's assume your HW doesn't have a FREERUN, can't you just create it by
>>>>> disconnecting all the sources?
>>>>
>>>> Yep, that's what we do.
>>>>
>>>
>>> No, you were saying that the mode doesn't exist and that your hardware
>>> doesn't
>>> support it. At the same time it can be achieved by manually disconnecting
>>> all
>>> the sources.
>>>
>>>>
>>>>> BTW, what chip are you using on mlx5 for this?
>>>>> I don't understand why the user would have to mangle state of all the pins
>>>>> just
>>>>> to stop dpll's work if he could just go into FREERUN and voila. Also what
>>>>> if
>>>>> user doesn't want change the configuration of the pins at all, and he just
>>>>> want
>>>>> to desynchronize it's dpll for i.e. testing reason.
>>>>
>>>> I tried to explain multiple times. Let the user have clean an abstracted
>>>> api, with clear semantics. Simple as that. Your internal freerun mode is
>>>> just something to abstract out, it is not needed to expose it.
>>>>
>>>
>>> Our hardware can support in total 4 modes, and 2 are now supported in ice.
>>> I don't get the idea for abstraction of hardware switches, modes or
>>> capabilities, and having those somehow achievable through different
>>> functionalities.
>>>
>>> I think we already discussed this long enough to make a decision..
>>> Though I am not convinced by your arguments, and you are not convinced by
>>> mine.
>>>
>>> Perhaps someone else could step in and cut the rope, so we could go further
>>> with this?
>>
>> Or, even better, please drop this for the initial patchset and have this
>> as a follow-up. Thanks!
>>
>>
> 
> On the responses from Jakub and Paolo, they supported the idea of having
> such mode.
> 
> Although Jakub have asked if there could be better name then FREERUN, also
> suggested DETACHED and STANDALONE.
> For me DETACHED seems pretty good, STANDALONE a bit too far..
> I am biased by the FREERUN from chip docs and don't have strong opinion
> on any of those..
> 
> Any suggestions?

It looks like we have a kind of split-brain situation, and my thoughts 
are following:
Even though right now we don't have any hardware supporting 
freerun/standalone mode, I do really like the idea to have it. It will 
be used in monitoring implementations where we refer to internal 
oscillator (Rb/Cs) as a source of truth to compare with the signal on 
the other pins. We can name it DETACHED if it sounds better.

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

* RE: [PATCH net-next 08/11] ice: add admin commands to access cgu configuration
  2023-07-24 17:21   ` Simon Horman
@ 2023-07-28 12:46       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 12:46 UTC (permalink / raw)
  To: Simon Horman, Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Simon Horman <simon.horman@corigine.com>
>Sent: Monday, July 24, 2023 7:22 PM
>
>On Thu, Jul 20, 2023 at 10:19:00AM +0100, Vadim Fedorenko wrote:
>
>...
>
>Hi Vadim,
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c
>b/drivers/net/ethernet/intel/ice/ice_common.c
>
>...
>
>> +/**
>> + * 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
>
>./scripts/kernel-doc says that @config is missing here.
>

Sure, will fix.

>> + * @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,
>> +			   u8 *dpll_state, u8 *config, s64 *phase_offset,
>> +			   u8 *eec_mode)
>
>...
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>
>...
>
>> +/**
>> + * 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
>
>Likewise, @eec_mode is missing here.

Sure, will fix.

>
>> + * @phase_offset: pointer to a buffer for returning phase offset
>> + * @dpll_state: state of the DPLL (output)
>
>And @mode is missing here.
>

Sure, will fix.

>> + *
>> + * 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 dpll_lock_status last_dpll_state, u8 *pin,
>> +		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
>> +		      enum dpll_lock_status *dpll_state,
>> +		      enum dpll_mode *mode)
>> +{
>> +	u8 hw_ref_state, hw_dpll_state, hw_eec_mode, hw_config;
>> +	s64 hw_phase_offset;
>> +	int status;
>> +
>> +	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
>> +					    &hw_dpll_state, &hw_config,
>> +					    &hw_phase_offset, &hw_eec_mode);
>> +	if (status) {
>> +		*dpll_state = ICE_CGU_STATE_INVALID;
>
>dpll_state is of type enum dpll_lock_status.
>But the type of ICE_CGU_STATE_INVALID is enum ice_cgu_state.
>Is this intended?
>
>As flagged by gcc-12 W=1 and clang-16 W=1 builds.
>

No it's leftover, thanks for catching!

>> +		return status;
>> +	}
>> +
>> +	if (pin)
>> +		/* current ref pin in dpll_state_refsel_status_X register */
>> +		*pin = hw_config & ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL;
>> +	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;
>
>Here dpll_state is checked for NULL.
>But, above, it is dereferenced in the case where ice_aq_get_cgu_dpll_status
>fails. Is that safe?
>

Yes, will fix.

>Also, perhaps it makes things a bit clearer to return 0 here.

True, will fix.

>
>...
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>
>...
>
>> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
>> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
>> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
>> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
>> +};
>
>A gcc-12 W=1 build warns that ice_e810t_sfp_cgu_inputs, and
>the similar static variables below, are unused when ice_ptp_hw.h
>is included in ice_main.c via ice.h.
>
>Looking at ice_e823_zl_cgu_outputs[], it seems to only be used
>in ice_ptp_hw.c, so perhaps it could be defined there.
>
>Perhaps that is also true of the other static variables below,
>but I didn't check that.

Yes, great catches, will fix.

Thank you for all of them!
Arkadiusz

>
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
>> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
>> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
>> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
>> +	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
>> +	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
>> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
>> +	{ "NONE",	  SI_REF0P, 0, 0 },
>> +	{ "NONE",	  SI_REF0N, 0, 0 },
>> +	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	  SI_REF2N, 0, 0 },
>> +	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
>> +	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> +	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
>> +	{ "NONE",	  ZL_REF0P, 0, 0 },
>> +	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "NONE",	  ZL_REF2P, 0, 0 },
>> +	{ "NONE",	  ZL_REF2N, 0, 0 },
>> +	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	  ZL_REF3N, 0, 0 },
>> +	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
>> +	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> +	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	   ZL_OUT5, 0, 0 },
>> +};
>> +
>>  extern const struct
>>  ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
>>
>
>...

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

* RE: [PATCH net-next 08/11] ice: add admin commands to access cgu configuration
@ 2023-07-28 12:46       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 12:46 UTC (permalink / raw)
  To: Simon Horman, Vadim Fedorenko
  Cc: Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche

>From: Simon Horman <simon.horman@corigine.com>
>Sent: Monday, July 24, 2023 7:22 PM
>
>On Thu, Jul 20, 2023 at 10:19:00AM +0100, Vadim Fedorenko wrote:
>
>...
>
>Hi Vadim,
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c
>b/drivers/net/ethernet/intel/ice/ice_common.c
>
>...
>
>> +/**
>> + * 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
>
>./scripts/kernel-doc says that @config is missing here.
>

Sure, will fix.

>> + * @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,
>> +			   u8 *dpll_state, u8 *config, s64 *phase_offset,
>> +			   u8 *eec_mode)
>
>...
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>
>...
>
>> +/**
>> + * 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
>
>Likewise, @eec_mode is missing here.

Sure, will fix.

>
>> + * @phase_offset: pointer to a buffer for returning phase offset
>> + * @dpll_state: state of the DPLL (output)
>
>And @mode is missing here.
>

Sure, will fix.

>> + *
>> + * 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 dpll_lock_status last_dpll_state, u8 *pin,
>> +		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
>> +		      enum dpll_lock_status *dpll_state,
>> +		      enum dpll_mode *mode)
>> +{
>> +	u8 hw_ref_state, hw_dpll_state, hw_eec_mode, hw_config;
>> +	s64 hw_phase_offset;
>> +	int status;
>> +
>> +	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
>> +					    &hw_dpll_state, &hw_config,
>> +					    &hw_phase_offset, &hw_eec_mode);
>> +	if (status) {
>> +		*dpll_state = ICE_CGU_STATE_INVALID;
>
>dpll_state is of type enum dpll_lock_status.
>But the type of ICE_CGU_STATE_INVALID is enum ice_cgu_state.
>Is this intended?
>
>As flagged by gcc-12 W=1 and clang-16 W=1 builds.
>

No it's leftover, thanks for catching!

>> +		return status;
>> +	}
>> +
>> +	if (pin)
>> +		/* current ref pin in dpll_state_refsel_status_X register */
>> +		*pin = hw_config & ICE_AQC_GET_CGU_DPLL_CONFIG_CLK_REF_SEL;
>> +	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;
>
>Here dpll_state is checked for NULL.
>But, above, it is dereferenced in the case where ice_aq_get_cgu_dpll_status
>fails. Is that safe?
>

Yes, will fix.

>Also, perhaps it makes things a bit clearer to return 0 here.

True, will fix.

>
>...
>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
>
>...
>
>> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
>> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
>> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
>> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
>> +};
>
>A gcc-12 W=1 build warns that ice_e810t_sfp_cgu_inputs, and
>the similar static variables below, are unused when ice_ptp_hw.h
>is included in ice_main.c via ice.h.
>
>Looking at ice_e823_zl_cgu_outputs[], it seems to only be used
>in ice_ptp_hw.c, so perhaps it could be defined there.
>
>Perhaps that is also true of the other static variables below,
>but I didn't check that.

Yes, great catches, will fix.

Thank you for all of them!
Arkadiusz

>
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
>> +	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
>> +	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
>> +	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
>> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
>> +	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
>> +	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
>> +	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
>> +	{ "NONE",	  SI_REF0P, 0, 0 },
>> +	{ "NONE",	  SI_REF0N, 0, 0 },
>> +	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	  SI_REF2N, 0, 0 },
>> +	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
>> +	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> +	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
>> +	{ "NONE",	  ZL_REF0P, 0, 0 },
>> +	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
>> +	{ "NONE",	  ZL_REF2P, 0, 0 },
>> +	{ "NONE",	  ZL_REF2N, 0, 0 },
>> +	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	  ZL_REF3N, 0, 0 },
>> +	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
>> +};
>> +
>> +static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
>> +	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
>> +	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
>> +	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
>> +	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
>> +		ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
>> +	{ "NONE",	   ZL_OUT5, 0, 0 },
>> +};
>> +
>>  extern const struct
>>  ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
>>
>
>...

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-21 11:39     ` Jiri Pirko
@ 2023-07-28 23:03       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 23:03 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 1:39 PM
>
>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]

>>+
>>+/**
>>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>>+ * @pf: private board structure
>>+ * @extack: error reporting
>>+ *
>>+ * 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.
>
>I don't know, I will probably need to beg you here. Why exactly are you
>ignoring my comments? It's not nice, I thought we are way past it...
>
>There is no "dead lock". Could you please describe how exactly
>the case you mention can happen? It can't.
>Could you please remove the trylock iteration below?
>It's completely pointless.
>

Yep, dead lock cannot happen now, will fix docs.
Will remove trylock.

>
>
>>+ *
>>+ * Return:
>>+ * 0 - if lock acquired
>>+ * negative - lock not acquired or dpll is not initialized
>>+ */
>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>*extack)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>
>And again, as I already told you, this flag checking is totally
>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>

This is not pointless, will explain below.

>
>

[...]

>>+/**
>>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ *
>>+ * Release resources of given pins array in the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++)
>>+		dpll_pin_put(pins[i].pin);
>>+}
>>+
>>+/**
>>+ * ice_dpll_get_pins - get pins from dpll subsystem
>>+ * @pf: board private structure
>>+ * @pins: pointer to pins array
>>+ * @start_idx: get starts from this pin idx value
>>+ * @count: number of pins
>>+ * @clock_id: clock_id of dpll device
>>+ *
>>+ * Get pins - allocate - in dpll subsystem, store them in pin field of
>>given
>>+ * pins array.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation failure reason
>>+ */
>>+static int
>>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>>+		  int start_idx, int count, u64 clock_id)
>>+{
>>+	int i, ret;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx,
>>THIS_MODULE,
>>+					   &pins[i].prop);
>>+		if (IS_ERR(pins[i].pin)) {
>>+			ret = PTR_ERR(pins[i].pin);
>>+			goto release_pins;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+
>>+release_pins:
>>+	while (--i >= 0)
>>+		dpll_pin_put(pins[i].pin);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>>+ * @dpll: dpll device pointer
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ *
>>+ * Unregister pins of a given array of pins from given dpll device
>>registered in
>>+ * dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void
>>+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+			 const struct dpll_pin_ops *ops, int count)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++)
>>+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @dpll: dpll pointer to register pins with
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ *
>>+ * Register pins of a given array with given dpll in dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+		       const struct dpll_pin_ops *ops, int count)
>>+{
>>+	int ret, i;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
>>+		if (ret)
>>+			goto unregister_pins;
>>+	}
>>+
>>+	return 0;
>>+
>>+unregister_pins:
>>+	while (--i >= 0)
>>+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * If cgu is owned unregister pins from given dplls.
>>+ * Release pins resources to the dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int
>>count,
>>+			    const struct dpll_pin_ops *ops,
>>+			    struct dpll_device *first,
>>+			    struct dpll_device *second)
>>+{
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(first, pins, ops, count);
>>+		ice_dpll_unregister_pins(second, pins, ops, count);
>>+	}
>>+	ice_dpll_release_pins(pins, count);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_direct_pins - initialize direct pins
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Allocate directly connected pins of a given array in dpll subsystem.
>>+ * If cgu is owned register allocated pins with given dplls.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>>+			  struct ice_dpll_pin *pins, int start_idx, int count,
>>+			  const struct dpll_pin_ops *ops,
>>+			  struct dpll_device *first, struct dpll_device *second)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf-
>>dplls.clock_id);
>>+	if (ret)
>>+		return ret;
>>+	if (cgu) {
>>+		ret = ice_dpll_register_pins(first, pins, ops, count);
>>+		if (ret)
>>+			goto release_pins;
>>+		ret = ice_dpll_register_pins(second, pins, ops, count);
>>+		if (ret)
>>+			goto unregister_first;
>>+	}
>>+
>>+	return 0;
>>+
>>+unregister_first:
>>+	ice_dpll_unregister_pins(first, pins, ops, count);
>>+release_pins:
>>+	ice_dpll_release_pins(pins, count);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>>+ * @pf: board private structure
>>+ *
>>+ * Deregister rclk pin from parent pins and release resources in dpll
>>subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+	struct dpll_pin *parent;
>>+	int i;
>>+
>>+	for (i = 0; i < rclk->num_parents; i++) {
>>+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>>+		if (!parent)
>>+			continue;
>>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>>+					   &ice_dpll_rclk_ops, rclk);
>>+	}
>>+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>>+		return;
>>+	netdev_dpll_pin_clear(vsi->netdev);
>>+	dpll_pin_put(rclk->pin);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>>+ * @pf: board private structure
>>+ * @pin: pin to register
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @ops: callback ops registered with the pins
>>+ *
>>+ * Allocate resource for recovered clock pin in dpll subsystem. Register
>>the
>>+ * pin with the parents it has in the info. Register pin with the pf's
>>main vsi
>>+ * netdev.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+			int start_idx, const struct dpll_pin_ops *ops)
>>+{
>>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+	struct dpll_pin *parent;
>>+	int ret, i;
>>+
>>+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>>+				pf->dplls.clock_id);
>>+	if (ret)
>>+		return ret;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>>+		if (!parent) {
>>+			ret = -ENODEV;
>>+			goto unregister_pins;
>>+		}
>>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>>+					       ops, &pf->dplls.rclk);
>>+		if (ret)
>>+			goto unregister_pins;
>>+	}
>>+	if (WARN_ON((!vsi || !vsi->netdev)))
>>+		return -EINVAL;
>>+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>>+
>>+	return 0;
>>+
>>+unregister_pins:
>>+	while (i) {
>>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>>+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>>+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
>>+	}
>>+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_pins - deinitialize direct pins
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is controlled by this pf
>>+ *
>>+ * If cgu is owned unregister directly connected pins from the dplls.
>>+ * Release resources of directly connected pins from the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
>>+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
>>+	int num_outputs = pf->dplls.num_outputs;
>>+	int num_inputs = pf->dplls.num_inputs;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+
>>+	ice_dpll_deinit_rclk_pin(pf);
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>>+					 num_inputs);
>>+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>>+					 num_inputs);
>>+	}
>>+	ice_dpll_release_pins(inputs, num_inputs);
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(dp->dpll, outputs,
>>+					 &ice_dpll_output_ops, num_outputs);
>>+		ice_dpll_unregister_pins(de->dpll, outputs,
>>+					 &ice_dpll_output_ops, num_outputs);
>>+		ice_dpll_release_pins(outputs, num_outputs);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Initialize directly connected pf's pins within pf's dplls in a Linux
>>dpll
>>+ * subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+	u32 rclk_idx;
>>+	int ret;
>>+
>>+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>>+					pf->dplls.num_inputs,
>>+					&ice_dpll_input_ops,
>>+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>>+	if (ret)
>>+		return ret;
>>+	if (cgu) {
>>+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>>+						pf->dplls.num_inputs,
>>+						pf->dplls.num_outputs,
>>+						&ice_dpll_output_ops,
>>+						pf->dplls.eec.dpll,
>>+						pf->dplls.pps.dpll);
>>+		if (ret)
>>+			goto deinit_inputs;
>>+	}
>>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf-
>>hw.pf_id;
>>+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>>+				      &ice_dpll_rclk_ops);
>>+	if (ret)
>>+		goto deinit_outputs;
>>+
>>+	return 0;
>>+deinit_outputs:
>>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>>+				    pf->dplls.num_outputs,
>>+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
>>+				    pf->dplls.eec.dpll);
>>+deinit_inputs:
>>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf-
>>dplls.num_inputs,
>>+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
>>+				    pf->dplls.eec.dpll);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>>+ * @pf: board private structure
>>+ * @d: pointer to ice_dpll
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * If cgu is owned unregister the dpll from dpll subsystem.
>>+ * Release resources of dpll device from dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void
>>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>>+{
>>+	if (cgu)
>>+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>>+	dpll_device_put(d->dpll);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>>+ * @pf: board private structure
>>+ * @d: dpll to be initialized
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @type: type of dpll being initialized
>>+ *
>>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is
>>controlled
>>+ * by this NIC, register dpll with the callback ops.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int
>>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>>+		   enum dpll_type type)
>>+{
>>+	u64 clock_id = pf->dplls.clock_id;
>>+	int ret;
>>+
>>+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>>+	if (IS_ERR(d->dpll)) {
>>+		ret = PTR_ERR(d->dpll);
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"dpll_device_get failed (%p) err=%d\n", d, ret);
>>+		return ret;
>>+	}
>>+	d->pf = pf;
>>+	if (cgu) {
>>+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>>+		if (ret) {
>>+			dpll_device_put(d->dpll);
>>+			return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>>+ * @pf: board private structure
>>+ *
>>+ * Stop dpll's kworker, release it's resources.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+
>>+	kthread_cancel_delayed_work_sync(&d->work);
>>+	kthread_destroy_worker(d->kworker);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>>+ * @pf: board private structure
>>+ *
>>+ * Create and start DPLLs periodic worker.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - create worker failure
>>+ */
>>+static int ice_dpll_init_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct kthread_worker *kworker;
>>+
>>+	ice_dpll_update_state(pf, &d->eec, true);
>>+	ice_dpll_update_state(pf, &d->pps, true);
>>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>>+					dev_name(ice_pf_to_dev(pf)));
>>+	if (IS_ERR(kworker))
>>+		return PTR_ERR(kworker);
>>+	d->kworker = kworker;
>>+	d->cgu_state_acq_err_num = 0;
>>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information for directly connected pins, cache them in pf's pins
>>+ * structures.
>>+ *
>>+ * Context: Called under pf->dplls.lock.
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>+			       enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>+	struct ice_hw *hw = &pf->hw;
>>+	struct ice_dpll_pin *pins;
>>+	int num_pins, i, ret;
>>+	u8 freq_supp_num;
>>+	bool input;
>>+
>>+	switch (pin_type) {
>>+	case ICE_DPLL_PIN_TYPE_INPUT:
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+		input = true;
>>+		break;
>>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+		input = false;
>>+		break;
>>+	default:
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>+		if (input) {
>>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>+						      &de->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>+						      &dp->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			pins[i].prop.capabilities |=
>>+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>+		}
>>+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>>+		if (ret)
>>+			return ret;
>>+		pins[i].prop.freq_supported =
>>+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>+		pins[i].prop.freq_supported_num = freq_supp_num;
>>+		pins[i].pf = pf;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
>>+ * @pf: board private structure
>>+ *
>>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
>>+
>>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+	pin->pf = pf;
>>+
>>+	return ice_dpll_pin_state_update(pf, pin,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins_info - init pins info wrapper
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Wraps functions for pin initialization.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type
>>pin_type)
>>+{
>>+	switch (pin_type) {
>>+	case ICE_DPLL_PIN_TYPE_INPUT:
>>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+		return ice_dpll_init_info_direct_pins(pf, pin_type);
>>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>+		return ice_dpll_init_info_rclk_pin(pf);
>>+	default:
>>+		return -EINVAL;
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_info - release memory allocated for pins info
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>>+{
>>+	kfree(pf->dplls.inputs);
>>+	kfree(pf->dplls.outputs);
>>+	kfree(pf->dplls.eec.input_prio);
>>+	kfree(pf->dplls.pps.input_prio);
>>+}
>>+
>>+/**
>>+ * 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).
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>>+{
>>+	struct ice_aqc_get_cgu_abilities abilities;
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_hw *hw = &pf->hw;
>>+	int ret, alloc_size, i;
>>+
>>+	d->clock_id = ice_generate_clock_id(pf);
>>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>+	if (ret) {
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to read cgu abilities\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>>+		return ret;
>>+	}
>>+
>>+	de->dpll_idx = abilities.eec_dpll_idx;
>>+	dp->dpll_idx = abilities.pps_dpll_idx;
>>+	d->num_inputs = abilities.num_inputs;
>>+	d->num_outputs = abilities.num_outputs;
>>+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>>+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>>+
>>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->inputs)
>>+		return -ENOMEM;
>>+
>>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!de->input_prio)
>>+		return -ENOMEM;
>>+
>>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!dp->input_prio)
>>+		return -ENOMEM;
>>+
>>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>>+	if (ret)
>>+		goto deinit_info;
>>+
>>+	if (cgu) {
>>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+		if (!d->outputs)
>>+			goto deinit_info;
>>+
>>+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+		if (ret)
>>+			goto deinit_info;
>>+	}
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>>+					&pf->dplls.rclk.num_parents);
>>+	if (ret)
>>+		return ret;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>+	if (ret)
>>+		return ret;
>>+	de->mode = DPLL_MODE_AUTOMATIC;
>>+	dp->mode = DPLL_MODE_AUTOMATIC;
>>+
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>>+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>>+
>>+	return 0;
>>+
>>+deinit_info:
>>+	dev_err(ice_pf_to_dev(pf),
>>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>d->outputs:%p\n",
>>+		__func__, d->inputs, de->input_prio,
>>+		dp->input_prio, d->outputs);
>>+	ice_dpll_deinit_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * Handles the cleanup work required after dpll initialization,freeing
>>resources
>>+ * and unregistering the dpll, pin and all resources used for handling
>>them.
>>+ *
>>+ * Context: Function holds pf->dplls.lock mutex.
>
>No it does not. Update your comments. Or better, remove them,
>they are totally useless anyway :/
>

Yes, will fix.

>
>>+ */
>>+void ice_dpll_deinit(struct ice_pf *pf)
>>+{
>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>+
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>+		return;
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+
>>+	ice_dpll_deinit_pins(pf, cgu);
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>+	ice_dpll_deinit_info(pf);
>>+	if (cgu)
>>+		ice_dpll_deinit_worker(pf);
>
>Could you please order the ice_dpll_deinit() to be symmetrical to
>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>ice_dpll_periodic_work() function is the only reason why you need it
>currently.
>

Not true.
The feature flag is common approach in ice. If the feature was successfully
initialized the flag is set. It allows to determine if deinit of the feature
is required on driver unload.

Right now the check for the flag is not only in kworker but also in each
callback, if the flag were cleared the data shall be not accessed by callbacks.
I know this is not required, but it helps on loading and unloading the driver,
thanks to that, spam of pin-get dump is not slowing the driver load/unload.

>
>>+	mutex_destroy(&pf->dplls.lock);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - initialize support for dpll subsystem
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device dplls, register them and pins connected within Linux
>>dpll
>>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of
>>DPLL
>>+ * configuration requests.
>>+ *
>>+ * Context: Function initializes and holds pf->dplls.lock mutex.
>
>No, it does not hold it.
>

Yes, will fix.

>
>>+ */
>>+void ice_dpll_init(struct ice_pf *pf)
>>+{
>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>+	struct ice_dplls *d = &pf->dplls;
>>+	int err = 0;
>>+
>>+	err = ice_dpll_init_info(pf, cgu);
>>+	if (err)
>>+		goto err_exit;
>>+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
>>+	if (err)
>>+		goto deinit_info;
>>+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
>>+	if (err)
>>+		goto deinit_eec;
>>+	err = ice_dpll_init_pins(pf, cgu);
>>+	if (err)
>>+		goto deinit_pps;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	if (cgu) {
>>+		err = ice_dpll_init_worker(pf);
>>+		if (err)
>>+			goto deinit_pins;
>>+	}
>>+
>>+	return;
>>+
>>+deinit_pins:
>>+	ice_dpll_deinit_pins(pf, cgu);
>>+deinit_pps:
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>+deinit_eec:
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>+deinit_info:
>>+	ice_dpll_deinit_info(pf);
>>+err_exit:
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	mutex_unlock(&d->lock);
>
>Leftover, please remove.
>

Yes, will fix.

>
>>+	mutex_destroy(&d->lock);
>>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,104 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#ifndef _ICE_DPLL_H_
>>+#define _ICE_DPLL_H_
>>+
>>+#include "ice.h"
>>+
>>+#define ICE_DPLL_PRIO_MAX	0xF
>>+#define ICE_DPLL_RCLK_NUM_MAX	4
>>+
>>+/** ice_dpll_pin - store info about pins
>>+ * @pin: dpll pin structure
>>+ * @pf: pointer to pf, which has registered the dpll_pin
>>+ * @idx: ice pin private idx
>>+ * @num_parents: hols number of parent pins
>>+ * @parent_idx: hold indexes of parent pins
>>+ * @flags: pin flags returned from HW
>>+ * @state: state of a pin
>>+ * @prop: pin properities
>>+ * @freq: current frequency of a pin
>>+ */
>>+struct ice_dpll_pin {
>>+	struct dpll_pin *pin;
>>+	struct ice_pf *pf;
>>+	u8 idx;
>>+	u8 num_parents;
>>+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>>+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>>+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>>+	struct dpll_pin_properties prop;
>>+	u32 freq;
>>+};
>>+
>>+/** ice_dpll - store info required for DPLL control
>>+ * @dpll: pointer to dpll dev
>>+ * @pf: pointer to pf, which has registered the dpll_device
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @input_idx: currently selected input index
>>+ * @prev_input_idx: previously selected input index
>>+ * @ref_state: state of dpll reference signals
>>+ * @eec_mode: eec_mode dpll is configured for
>>+ * @phase_shift: phase shift delay of a dpll
>>+ * @input_prio: priorities of each input
>>+ * @dpll_state: current dpll sync state
>>+ * @prev_dpll_state: last dpll sync state
>>+ * @active_input: pointer to active input pin
>>+ * @prev_input: pointer to previous active input pin
>>+ */
>>+struct ice_dpll {
>>+	struct dpll_device *dpll;
>>+	struct ice_pf *pf;
>>+	u8 dpll_idx;
>>+	u8 input_idx;
>>+	u8 prev_input_idx;
>>+	u8 ref_state;
>>+	u8 eec_mode;
>>+	s64 phase_shift;
>>+	u8 *input_prio;
>>+	enum dpll_lock_status dpll_state;
>>+	enum dpll_lock_status prev_dpll_state;
>>+	enum dpll_mode mode;
>>+	struct dpll_pin *active_input;
>>+	struct dpll_pin *prev_input;
>>+};
>>+
>>+/** ice_dplls - store info required for CCU (clock controlling unit)
>>+ * @kworker: periodic worker
>>+ * @work: periodic work
>>+ * @lock: locks access to configuration of a dpll
>>+ * @eec: pointer to EEC dpll dev
>>+ * @pps: pointer to PPS dpll dev
>>+ * @inputs: input pins pointer
>>+ * @outputs: output pins pointer
>>+ * @rclk: recovered pins pointer
>>+ * @num_inputs: number of input pins available on dpll
>>+ * @num_outputs: number of output pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>>+ * @clock_id: clock_id of dplls
>>+ */
>>+struct ice_dplls {
>>+	struct kthread_worker *kworker;
>>+	struct kthread_delayed_work work;
>>+	struct mutex lock;
>>+	struct ice_dpll eec;
>>+	struct ice_dpll pps;
>>+	struct ice_dpll_pin *inputs;
>>+	struct ice_dpll_pin *outputs;
>>+	struct ice_dpll_pin rclk;
>>+	u8 num_inputs;
>>+	u8 num_outputs;
>>+	int cgu_state_acq_err_num;
>>+	u8 base_rclk_idx;
>>+	u64 clock_id;
>>+	s32 input_phase_adj_max;
>>+	s32 output_phase_adj_max;
>>+};
>>+
>>+void ice_dpll_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_deinit(struct ice_pf *pf);
>>+
>>+#endif
>>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
>>b/drivers/net/ethernet/intel/ice/ice_main.c
>>index 19a5e7f3a075..0a94daaf3d20 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4613,6 +4613,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");
>>@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
>> 		ice_gnss_exit(pf);
>> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
>> 		ice_ptp_release(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>>+	    ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_deinit(pf);
>> }
>>
>> static void ice_init_wakeup(struct ice_pf *pf)
>>--
>>2.27.0
>>

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-28 23:03       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 23:03 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, Olech, Milena,
	Michalik, Michal, linux-arm-kernel, poros, mschmidt, netdev,
	linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, July 21, 2023 1:39 PM
>
>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]

>>+
>>+/**
>>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>>+ * @pf: private board structure
>>+ * @extack: error reporting
>>+ *
>>+ * 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.
>
>I don't know, I will probably need to beg you here. Why exactly are you
>ignoring my comments? It's not nice, I thought we are way past it...
>
>There is no "dead lock". Could you please describe how exactly
>the case you mention can happen? It can't.
>Could you please remove the trylock iteration below?
>It's completely pointless.
>

Yep, dead lock cannot happen now, will fix docs.
Will remove trylock.

>
>
>>+ *
>>+ * Return:
>>+ * 0 - if lock acquired
>>+ * negative - lock not acquired or dpll is not initialized
>>+ */
>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>*extack)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>
>And again, as I already told you, this flag checking is totally
>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>

This is not pointless, will explain below.

>
>

[...]

>>+/**
>>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ *
>>+ * Release resources of given pins array in the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++)
>>+		dpll_pin_put(pins[i].pin);
>>+}
>>+
>>+/**
>>+ * ice_dpll_get_pins - get pins from dpll subsystem
>>+ * @pf: board private structure
>>+ * @pins: pointer to pins array
>>+ * @start_idx: get starts from this pin idx value
>>+ * @count: number of pins
>>+ * @clock_id: clock_id of dpll device
>>+ *
>>+ * Get pins - allocate - in dpll subsystem, store them in pin field of
>>given
>>+ * pins array.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation failure reason
>>+ */
>>+static int
>>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>>+		  int start_idx, int count, u64 clock_id)
>>+{
>>+	int i, ret;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		pins[i].pin = dpll_pin_get(clock_id, i + start_idx,
>>THIS_MODULE,
>>+					   &pins[i].prop);
>>+		if (IS_ERR(pins[i].pin)) {
>>+			ret = PTR_ERR(pins[i].pin);
>>+			goto release_pins;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+
>>+release_pins:
>>+	while (--i >= 0)
>>+		dpll_pin_put(pins[i].pin);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>>+ * @dpll: dpll device pointer
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ *
>>+ * Unregister pins of a given array of pins from given dpll device
>>registered in
>>+ * dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void
>>+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+			 const struct dpll_pin_ops *ops, int count)
>>+{
>>+	int i;
>>+
>>+	for (i = 0; i < count; i++)
>>+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @dpll: dpll pointer to register pins with
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ *
>>+ * Register pins of a given array with given dpll in dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+		       const struct dpll_pin_ops *ops, int count)
>>+{
>>+	int ret, i;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
>>+		if (ret)
>>+			goto unregister_pins;
>>+	}
>>+
>>+	return 0;
>>+
>>+unregister_pins:
>>+	while (--i >= 0)
>>+		dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * If cgu is owned unregister pins from given dplls.
>>+ * Release pins resources to the dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int
>>count,
>>+			    const struct dpll_pin_ops *ops,
>>+			    struct dpll_device *first,
>>+			    struct dpll_device *second)
>>+{
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(first, pins, ops, count);
>>+		ice_dpll_unregister_pins(second, pins, ops, count);
>>+	}
>>+	ice_dpll_release_pins(pins, count);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_direct_pins - initialize direct pins
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Allocate directly connected pins of a given array in dpll subsystem.
>>+ * If cgu is owned register allocated pins with given dplls.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>>+			  struct ice_dpll_pin *pins, int start_idx, int count,
>>+			  const struct dpll_pin_ops *ops,
>>+			  struct dpll_device *first, struct dpll_device *second)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf-
>>dplls.clock_id);
>>+	if (ret)
>>+		return ret;
>>+	if (cgu) {
>>+		ret = ice_dpll_register_pins(first, pins, ops, count);
>>+		if (ret)
>>+			goto release_pins;
>>+		ret = ice_dpll_register_pins(second, pins, ops, count);
>>+		if (ret)
>>+			goto unregister_first;
>>+	}
>>+
>>+	return 0;
>>+
>>+unregister_first:
>>+	ice_dpll_unregister_pins(first, pins, ops, count);
>>+release_pins:
>>+	ice_dpll_release_pins(pins, count);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>>+ * @pf: board private structure
>>+ *
>>+ * Deregister rclk pin from parent pins and release resources in dpll
>>subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+	struct dpll_pin *parent;
>>+	int i;
>>+
>>+	for (i = 0; i < rclk->num_parents; i++) {
>>+		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>>+		if (!parent)
>>+			continue;
>>+		dpll_pin_on_pin_unregister(parent, rclk->pin,
>>+					   &ice_dpll_rclk_ops, rclk);
>>+	}
>>+	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>>+		return;
>>+	netdev_dpll_pin_clear(vsi->netdev);
>>+	dpll_pin_put(rclk->pin);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>>+ * @pf: board private structure
>>+ * @pin: pin to register
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @ops: callback ops registered with the pins
>>+ *
>>+ * Allocate resource for recovered clock pin in dpll subsystem. Register
>>the
>>+ * pin with the parents it has in the info. Register pin with the pf's
>>main vsi
>>+ * netdev.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+			int start_idx, const struct dpll_pin_ops *ops)
>>+{
>>+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+	struct dpll_pin *parent;
>>+	int ret, i;
>>+
>>+	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>>+				pf->dplls.clock_id);
>>+	if (ret)
>>+		return ret;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>>+		if (!parent) {
>>+			ret = -ENODEV;
>>+			goto unregister_pins;
>>+		}
>>+		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>>+					       ops, &pf->dplls.rclk);
>>+		if (ret)
>>+			goto unregister_pins;
>>+	}
>>+	if (WARN_ON((!vsi || !vsi->netdev)))
>>+		return -EINVAL;
>>+	netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>>+
>>+	return 0;
>>+
>>+unregister_pins:
>>+	while (i) {
>>+		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>>+		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>>+					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
>>+	}
>>+	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_pins - deinitialize direct pins
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is controlled by this pf
>>+ *
>>+ * If cgu is owned unregister directly connected pins from the dplls.
>>+ * Release resources of directly connected pins from the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+	struct ice_dpll_pin *outputs = pf->dplls.outputs;
>>+	struct ice_dpll_pin *inputs = pf->dplls.inputs;
>>+	int num_outputs = pf->dplls.num_outputs;
>>+	int num_inputs = pf->dplls.num_inputs;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+
>>+	ice_dpll_deinit_rclk_pin(pf);
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>>+					 num_inputs);
>>+		ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>>+					 num_inputs);
>>+	}
>>+	ice_dpll_release_pins(inputs, num_inputs);
>>+	if (cgu) {
>>+		ice_dpll_unregister_pins(dp->dpll, outputs,
>>+					 &ice_dpll_output_ops, num_outputs);
>>+		ice_dpll_unregister_pins(de->dpll, outputs,
>>+					 &ice_dpll_output_ops, num_outputs);
>>+		ice_dpll_release_pins(outputs, num_outputs);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Initialize directly connected pf's pins within pf's dplls in a Linux
>>dpll
>>+ * subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+	u32 rclk_idx;
>>+	int ret;
>>+
>>+	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>>+					pf->dplls.num_inputs,
>>+					&ice_dpll_input_ops,
>>+					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>>+	if (ret)
>>+		return ret;
>>+	if (cgu) {
>>+		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>>+						pf->dplls.num_inputs,
>>+						pf->dplls.num_outputs,
>>+						&ice_dpll_output_ops,
>>+						pf->dplls.eec.dpll,
>>+						pf->dplls.pps.dpll);
>>+		if (ret)
>>+			goto deinit_inputs;
>>+	}
>>+	rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf-
>>hw.pf_id;
>>+	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>>+				      &ice_dpll_rclk_ops);
>>+	if (ret)
>>+		goto deinit_outputs;
>>+
>>+	return 0;
>>+deinit_outputs:
>>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>>+				    pf->dplls.num_outputs,
>>+				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
>>+				    pf->dplls.eec.dpll);
>>+deinit_inputs:
>>+	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf-
>>dplls.num_inputs,
>>+				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
>>+				    pf->dplls.eec.dpll);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>>+ * @pf: board private structure
>>+ * @d: pointer to ice_dpll
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * If cgu is owned unregister the dpll from dpll subsystem.
>>+ * Release resources of dpll device from dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void
>>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>>+{
>>+	if (cgu)
>>+		dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>>+	dpll_device_put(d->dpll);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>>+ * @pf: board private structure
>>+ * @d: dpll to be initialized
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @type: type of dpll being initialized
>>+ *
>>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is
>>controlled
>>+ * by this NIC, register dpll with the callback ops.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int
>>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>>+		   enum dpll_type type)
>>+{
>>+	u64 clock_id = pf->dplls.clock_id;
>>+	int ret;
>>+
>>+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>>+	if (IS_ERR(d->dpll)) {
>>+		ret = PTR_ERR(d->dpll);
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"dpll_device_get failed (%p) err=%d\n", d, ret);
>>+		return ret;
>>+	}
>>+	d->pf = pf;
>>+	if (cgu) {
>>+		ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>>+		if (ret) {
>>+			dpll_device_put(d->dpll);
>>+			return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>>+ * @pf: board private structure
>>+ *
>>+ * Stop dpll's kworker, release it's resources.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+
>>+	kthread_cancel_delayed_work_sync(&d->work);
>>+	kthread_destroy_worker(d->kworker);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>>+ * @pf: board private structure
>>+ *
>>+ * Create and start DPLLs periodic worker.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - create worker failure
>>+ */
>>+static int ice_dpll_init_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct kthread_worker *kworker;
>>+
>>+	ice_dpll_update_state(pf, &d->eec, true);
>>+	ice_dpll_update_state(pf, &d->pps, true);
>>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>>+					dev_name(ice_pf_to_dev(pf)));
>>+	if (IS_ERR(kworker))
>>+		return PTR_ERR(kworker);
>>+	d->kworker = kworker;
>>+	d->cgu_state_acq_err_num = 0;
>>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information for directly connected pins, cache them in pf's pins
>>+ * structures.
>>+ *
>>+ * Context: Called under pf->dplls.lock.
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>+			       enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>+	struct ice_hw *hw = &pf->hw;
>>+	struct ice_dpll_pin *pins;
>>+	int num_pins, i, ret;
>>+	u8 freq_supp_num;
>>+	bool input;
>>+
>>+	switch (pin_type) {
>>+	case ICE_DPLL_PIN_TYPE_INPUT:
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+		input = true;
>>+		break;
>>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+		input = false;
>>+		break;
>>+	default:
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>+		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>+		if (input) {
>>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>+						      &de->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>+						      &dp->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			pins[i].prop.capabilities |=
>>+				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>+		}
>>+		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>>+		if (ret)
>>+			return ret;
>>+		pins[i].prop.freq_supported =
>>+			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>+		pins[i].prop.freq_supported_num = freq_supp_num;
>>+		pins[i].pf = pf;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info_rclk_pin - initializes rclk pin information
>>+ * @pf: board private structure
>>+ *
>>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *pin = &pf->dplls.rclk;
>>+
>>+	pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+	pin->pf = pf;
>>+
>>+	return ice_dpll_pin_state_update(pf, pin,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins_info - init pins info wrapper
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Wraps functions for pin initialization.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type
>>pin_type)
>>+{
>>+	switch (pin_type) {
>>+	case ICE_DPLL_PIN_TYPE_INPUT:
>>+	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+		return ice_dpll_init_info_direct_pins(pf, pin_type);
>>+	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>+		return ice_dpll_init_info_rclk_pin(pf);
>>+	default:
>>+		return -EINVAL;
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_info - release memory allocated for pins info
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ */
>>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>>+{
>>+	kfree(pf->dplls.inputs);
>>+	kfree(pf->dplls.outputs);
>>+	kfree(pf->dplls.eec.input_prio);
>>+	kfree(pf->dplls.pps.input_prio);
>>+}
>>+
>>+/**
>>+ * 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).
>>+ *
>>+ * Context: Called under pf->dplls.lock
>
>No, it is not.
>

Yes, will fix.

>
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>>+{
>>+	struct ice_aqc_get_cgu_abilities abilities;
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_hw *hw = &pf->hw;
>>+	int ret, alloc_size, i;
>>+
>>+	d->clock_id = ice_generate_clock_id(pf);
>>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>+	if (ret) {
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to read cgu abilities\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>>+		return ret;
>>+	}
>>+
>>+	de->dpll_idx = abilities.eec_dpll_idx;
>>+	dp->dpll_idx = abilities.pps_dpll_idx;
>>+	d->num_inputs = abilities.num_inputs;
>>+	d->num_outputs = abilities.num_outputs;
>>+	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>>+	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>>+
>>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->inputs)
>>+		return -ENOMEM;
>>+
>>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!de->input_prio)
>>+		return -ENOMEM;
>>+
>>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!dp->input_prio)
>>+		return -ENOMEM;
>>+
>>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>>+	if (ret)
>>+		goto deinit_info;
>>+
>>+	if (cgu) {
>>+		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+		if (!d->outputs)
>>+			goto deinit_info;
>>+
>>+		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+		if (ret)
>>+			goto deinit_info;
>>+	}
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>>+					&pf->dplls.rclk.num_parents);
>>+	if (ret)
>>+		return ret;
>>+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>>+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>+	if (ret)
>>+		return ret;
>>+	de->mode = DPLL_MODE_AUTOMATIC;
>>+	dp->mode = DPLL_MODE_AUTOMATIC;
>>+
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>>+		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>>+
>>+	return 0;
>>+
>>+deinit_info:
>>+	dev_err(ice_pf_to_dev(pf),
>>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>d->outputs:%p\n",
>>+		__func__, d->inputs, de->input_prio,
>>+		dp->input_prio, d->outputs);
>>+	ice_dpll_deinit_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * Handles the cleanup work required after dpll initialization,freeing
>>resources
>>+ * and unregistering the dpll, pin and all resources used for handling
>>them.
>>+ *
>>+ * Context: Function holds pf->dplls.lock mutex.
>
>No it does not. Update your comments. Or better, remove them,
>they are totally useless anyway :/
>

Yes, will fix.

>
>>+ */
>>+void ice_dpll_deinit(struct ice_pf *pf)
>>+{
>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>+
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>+		return;
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+
>>+	ice_dpll_deinit_pins(pf, cgu);
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>+	ice_dpll_deinit_info(pf);
>>+	if (cgu)
>>+		ice_dpll_deinit_worker(pf);
>
>Could you please order the ice_dpll_deinit() to be symmetrical to
>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>ice_dpll_periodic_work() function is the only reason why you need it
>currently.
>

Not true.
The feature flag is common approach in ice. If the feature was successfully
initialized the flag is set. It allows to determine if deinit of the feature
is required on driver unload.

Right now the check for the flag is not only in kworker but also in each
callback, if the flag were cleared the data shall be not accessed by callbacks.
I know this is not required, but it helps on loading and unloading the driver,
thanks to that, spam of pin-get dump is not slowing the driver load/unload.

>
>>+	mutex_destroy(&pf->dplls.lock);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - initialize support for dpll subsystem
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device dplls, register them and pins connected within Linux
>>dpll
>>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of
>>DPLL
>>+ * configuration requests.
>>+ *
>>+ * Context: Function initializes and holds pf->dplls.lock mutex.
>
>No, it does not hold it.
>

Yes, will fix.

>
>>+ */
>>+void ice_dpll_init(struct ice_pf *pf)
>>+{
>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>+	struct ice_dplls *d = &pf->dplls;
>>+	int err = 0;
>>+
>>+	err = ice_dpll_init_info(pf, cgu);
>>+	if (err)
>>+		goto err_exit;
>>+	err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
>>+	if (err)
>>+		goto deinit_info;
>>+	err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
>>+	if (err)
>>+		goto deinit_eec;
>>+	err = ice_dpll_init_pins(pf, cgu);
>>+	if (err)
>>+		goto deinit_pps;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	if (cgu) {
>>+		err = ice_dpll_init_worker(pf);
>>+		if (err)
>>+			goto deinit_pins;
>>+	}
>>+
>>+	return;
>>+
>>+deinit_pins:
>>+	ice_dpll_deinit_pins(pf, cgu);
>>+deinit_pps:
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>+deinit_eec:
>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>+deinit_info:
>>+	ice_dpll_deinit_info(pf);
>>+err_exit:
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	mutex_unlock(&d->lock);
>
>Leftover, please remove.
>

Yes, will fix.

>
>>+	mutex_destroy(&d->lock);
>>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", 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..975066b71c5e
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,104 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#ifndef _ICE_DPLL_H_
>>+#define _ICE_DPLL_H_
>>+
>>+#include "ice.h"
>>+
>>+#define ICE_DPLL_PRIO_MAX	0xF
>>+#define ICE_DPLL_RCLK_NUM_MAX	4
>>+
>>+/** ice_dpll_pin - store info about pins
>>+ * @pin: dpll pin structure
>>+ * @pf: pointer to pf, which has registered the dpll_pin
>>+ * @idx: ice pin private idx
>>+ * @num_parents: hols number of parent pins
>>+ * @parent_idx: hold indexes of parent pins
>>+ * @flags: pin flags returned from HW
>>+ * @state: state of a pin
>>+ * @prop: pin properities
>>+ * @freq: current frequency of a pin
>>+ */
>>+struct ice_dpll_pin {
>>+	struct dpll_pin *pin;
>>+	struct ice_pf *pf;
>>+	u8 idx;
>>+	u8 num_parents;
>>+	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>>+	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>>+	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>>+	struct dpll_pin_properties prop;
>>+	u32 freq;
>>+};
>>+
>>+/** ice_dpll - store info required for DPLL control
>>+ * @dpll: pointer to dpll dev
>>+ * @pf: pointer to pf, which has registered the dpll_device
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @input_idx: currently selected input index
>>+ * @prev_input_idx: previously selected input index
>>+ * @ref_state: state of dpll reference signals
>>+ * @eec_mode: eec_mode dpll is configured for
>>+ * @phase_shift: phase shift delay of a dpll
>>+ * @input_prio: priorities of each input
>>+ * @dpll_state: current dpll sync state
>>+ * @prev_dpll_state: last dpll sync state
>>+ * @active_input: pointer to active input pin
>>+ * @prev_input: pointer to previous active input pin
>>+ */
>>+struct ice_dpll {
>>+	struct dpll_device *dpll;
>>+	struct ice_pf *pf;
>>+	u8 dpll_idx;
>>+	u8 input_idx;
>>+	u8 prev_input_idx;
>>+	u8 ref_state;
>>+	u8 eec_mode;
>>+	s64 phase_shift;
>>+	u8 *input_prio;
>>+	enum dpll_lock_status dpll_state;
>>+	enum dpll_lock_status prev_dpll_state;
>>+	enum dpll_mode mode;
>>+	struct dpll_pin *active_input;
>>+	struct dpll_pin *prev_input;
>>+};
>>+
>>+/** ice_dplls - store info required for CCU (clock controlling unit)
>>+ * @kworker: periodic worker
>>+ * @work: periodic work
>>+ * @lock: locks access to configuration of a dpll
>>+ * @eec: pointer to EEC dpll dev
>>+ * @pps: pointer to PPS dpll dev
>>+ * @inputs: input pins pointer
>>+ * @outputs: output pins pointer
>>+ * @rclk: recovered pins pointer
>>+ * @num_inputs: number of input pins available on dpll
>>+ * @num_outputs: number of output pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>>+ * @clock_id: clock_id of dplls
>>+ */
>>+struct ice_dplls {
>>+	struct kthread_worker *kworker;
>>+	struct kthread_delayed_work work;
>>+	struct mutex lock;
>>+	struct ice_dpll eec;
>>+	struct ice_dpll pps;
>>+	struct ice_dpll_pin *inputs;
>>+	struct ice_dpll_pin *outputs;
>>+	struct ice_dpll_pin rclk;
>>+	u8 num_inputs;
>>+	u8 num_outputs;
>>+	int cgu_state_acq_err_num;
>>+	u8 base_rclk_idx;
>>+	u64 clock_id;
>>+	s32 input_phase_adj_max;
>>+	s32 output_phase_adj_max;
>>+};
>>+
>>+void ice_dpll_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_deinit(struct ice_pf *pf);
>>+
>>+#endif
>>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
>>b/drivers/net/ethernet/intel/ice/ice_main.c
>>index 19a5e7f3a075..0a94daaf3d20 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4613,6 +4613,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");
>>@@ -4639,6 +4643,9 @@ static void ice_deinit_features(struct ice_pf *pf)
>> 		ice_gnss_exit(pf);
>> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
>> 		ice_ptp_release(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>>+	    ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_deinit(pf);
>> }
>>
>> static void ice_init_wakeup(struct ice_pf *pf)
>>--
>>2.27.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] 109+ messages in thread

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-24 17:58     ` Vadim Fedorenko
@ 2023-07-28 23:10         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 23:10 UTC (permalink / raw)
  To: Vadim Fedorenko, Simon Horman
  Cc: Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche


>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Sent: Monday, July 24, 2023 7:59 PM
>
>On 24.07.2023 18:41, Simon Horman wrote:
>> On Thu, Jul 20, 2023 at 10:19:01AM +0100, Vadim Fedorenko wrote:
>>
>> ...
>>
>> Hi Vadim,
>>
>
>Hi Simon!
>Thanks for the review. I believe Arkadiusz as the author of the patch will
>adjust the code accordingly
>

Yes, will fix all the findings, thank you Simon for pointing them out!
Arkadiusz

>>> +/**
>>> + * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>>> + * @pf: private board structure
>>> + *
>>> + * Unlock the mutex from the callback operations invoked by dpll
>>>subsystem.
>>> + */
>>> +static void ice_dpll_cb_unlock(struct ice_pf *pf)
>>> +{
>>> +	mutex_unlock(&pf->dplls.lock);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_freq_set - set pin's frequency
>>> + * @pf: private board structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being configured
>>> + * @freq: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Set requested frequency on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error on AQ or wrong pin type given
>>> + */
>>> +static int
>>> +ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>> +		      enum ice_dpll_pin_type pin_type, const u32 freq,
>>> +		      struct netlink_ext_ack *extack)
>>> +{
>>> +	int ret;
>>> +	u8 flags;
>>
>> Please arrange local variable declarations for new Networking
>> code in reverse xmas tree order - longest line to shortest.
>>
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>>> +		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>>> +					       pin->flags[0], freq, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>> +		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>> +						0, freq, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret) {
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to set pin freq:%u on
>>>pin:%u\n",
>>> +				   ret,
>>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				   freq, pin->idx);
>>> +		return ret;
>>> +	}
>>> +	pin->freq = freq;
>>> +
>>> +	return 0;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_pin_state_update - update pin's state
>>> + * @pf: private board struct
>>> + * @pin: structure with pin attributes to be updated
>>> + * @pin_type: type of pin being updated
>>> + * @extack: error reporting
>>> + *
>>> + * Determine pin current state and frequency, then update struct
>>> + * holding the pin info. For input pin states are separated for each
>>> + * dpll, for rclk pins states are separated for each parent.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +int
>>> +ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>> +			  enum ice_dpll_pin_type pin_type,
>>> +			  struct netlink_ext_ack *extack)
>>
>>> +/**
>>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       const u32 frequency,
>>> +		       struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_set - output pin callback for set
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	*frequency = p->freq;
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a input pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_get - output pin callback for get
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_enable - enable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being enabled
>>> + * @extack: error reporting
>>> + *
>>> + * Enable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		    enum ice_dpll_pin_type pin_type,
>>> +		    struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to enable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_disable - disable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being disabled
>>> + * @extack: error reporting
>>> + *
>>> + * Disable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		     enum ice_dpll_pin_type pin_type,
>>> +		     struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to disable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>
>>> +/**
>>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       const u32 frequency,
>>> +		       struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_set - output pin callback for set
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	*frequency = p->freq;
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a input pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_get - output pin callback for get
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_enable - enable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being enabled
>>> + * @extack: error reporting
>>> + *
>>> + * Enable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		    enum ice_dpll_pin_type pin_type,
>>> +		    struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to enable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_disable - disable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being disabled
>>> + * @extack: error reporting
>>> + *
>>> + * Disable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		     enum ice_dpll_pin_type pin_type,
>>> +		     struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to disable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>
>> Should this function be static?
>>
>>> +{
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>>> +					       NULL, &pin->flags[0],
>>> +					       &pin->freq, NULL);
>>> +		if (ret)
>>> +			goto err;
>>> +		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>>> +			if (pin->pin) {
>>> +				pin->state[pf->dplls.eec.dpll_idx] =
>>> +					pin->pin == pf->dplls.eec.active_input ?
>>> +					DPLL_PIN_STATE_CONNECTED :
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +				pin->state[pf->dplls.pps.dpll_idx] =
>>> +					pin->pin == pf->dplls.pps.active_input ?
>>> +					DPLL_PIN_STATE_CONNECTED :
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +			} else {
>>> +				pin->state[pf->dplls.eec.dpll_idx] =
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +				pin->state[pf->dplls.pps.dpll_idx] =
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +			}
>>> +		} else {
>>> +			pin->state[pf->dplls.eec.dpll_idx] =
>>> +				DPLL_PIN_STATE_DISCONNECTED;
>>> +			pin->state[pf->dplls.pps.dpll_idx] =
>>> +				DPLL_PIN_STATE_DISCONNECTED;
>>> +		}
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>>> +						&pin->flags[0], NULL,
>>> +						&pin->freq, NULL);
>>> +		if (ret)
>>> +			goto err;
>>> +		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>>> +			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>>> +		else
>>> +			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>
>> clang-16 complains that:
>>
>>    drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: expected
>>expression
>>                    u8 parent, port_num =
>>ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>>
>> Which, I think means, it wants this case to be enclosed in { }
>>
>>> +		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>>> +
>>> +		for (parent = 0; parent < pf->dplls.rclk.num_parents;
>>> +		     parent++) {
>>> +			u8 p = parent;
>>> +
>>> +			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
>>> +							 &port_num,
>>> +							 &pin->flags[parent],
>>> +							 NULL);
>>> +			if (ret)
>>> +				goto err;
>>> +			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>>> +			    pin->flags[parent])
>>> +				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>>> +			else
>>> +				pin->state[parent] =
>>> +					DPLL_PIN_STATE_DISCONNECTED;
>>> +		}
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return 0;
>>> +err:
>>> +	if (extack)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to update %s pin:%u\n",
>>> +				   ret,
>>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +	else
>>> +		dev_err_ratelimited(ice_pf_to_dev(pf),
>>> +				    "err:%d %s failed to update %s pin:%u\n",
>>> +				    ret,
>>> +				    ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				    pin_type_name[pin_type], pin->idx);
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_update_state - update dpll state
>>> + * @pf: pf private structure
>>> + * @d: pointer to queried dpll device
>>> + * @init: if function called on initialization of ice dpll
>>> + *
>>> + * Poll current state of dpll from hw and update ice_dpll struct.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - AQ failure
>>> + */
>>> +static int
>>> +ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>>> +{
>>> +	struct ice_dpll_pin *p = NULL;
>>> +	int ret;
>>> +
>>> +	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>>> +				&d->input_idx, &d->ref_state, &d->eec_mode,
>>> +				&d->phase_shift, &d->dpll_state, &d->mode);
>>> +
>>> +	dev_dbg(ice_pf_to_dev(pf),
>>> +		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d
>>>mode:%d\n",
>>> +		d->dpll_idx, d->prev_input_idx, d->input_idx,
>>> +		d->dpll_state, d->prev_dpll_state, d->mode);
>>> +	if (ret) {
>>> +		dev_err(ice_pf_to_dev(pf),
>>> +			"update dpll=%d state failed, ret=%d %s\n",
>>> +			d->dpll_idx, ret,
>>> +			ice_aq_str(pf->hw.adminq.sq_last_status));
>>> +		return ret;
>>> +	}
>>> +	if (init) {
>>> +		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
>>> +		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
>>
>> Should this be '||' rather than '&&' ?
>>
>> Flagged by a clang-16 W=1 build, Sparse and Smatch.
>>
>>> +			d->active_input = pf->dplls.inputs[d->input_idx].pin;
>>> +		p = &pf->dplls.inputs[d->input_idx];
>>> +		return ice_dpll_pin_state_update(pf, p,
>>> +						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
>>> +	}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_init_info_direct_pins - initializes direct pins info
>>> + * @pf: board private structure
>>> + * @pin_type: type of pins being initialized
>>> + *
>>> + * Init information for directly connected pins, cache them in pf's
>>>pins
>>> + * structures.
>>> + *
>>> + * Context: Called under pf->dplls.lock.
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - init failure reason
>>> + */
>>> +static int
>>> +ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>> +			       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>> +	struct ice_hw *hw = &pf->hw;
>>> +	struct ice_dpll_pin *pins;
>>> +	int num_pins, i, ret;
>>> +	u8 freq_supp_num;
>>> +	bool input;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		pins = pf->dplls.inputs;
>>> +		num_pins = pf->dplls.num_inputs;
>>> +		input = true;
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		pins = pf->dplls.outputs;
>>> +		num_pins = pf->dplls.num_outputs;
>>> +		input = false;
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	for (i = 0; i < num_pins; i++) {
>>> +		pins[i].idx = i;
>>> +		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>> +		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>> +		if (input) {
>>> +			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>> +						      &de->input_prio[i]);
>>> +			if (ret)
>>> +				return ret;
>>> +			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>> +						      &dp->input_prio[i]);
>>> +			if (ret)
>>> +				return ret;
>>> +			pins[i].prop.capabilities |=
>>> +				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>> +		}
>>> +		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>> +		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>>> +		if (ret)
>>> +			return ret;
>>> +		pins[i].prop.freq_supported =
>>> +			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>> +		pins[i].prop.freq_supported_num = freq_supp_num;
>>> +		pins[i].pf = pf;
>>> +	}
>>> +
>>
>> I'm unsure if this can happen,
>> but if the for loop above iterates zero times
>> then ret will be null here.
>>
>> Use of uninitialised variable flagged by Smatch.
>>
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * 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).
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - init failure reason
>>> + */
>>> +static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>>> +{
>>> +	struct ice_aqc_get_cgu_abilities abilities;
>>> +	struct ice_dpll *de = &pf->dplls.eec;
>>> +	struct ice_dpll *dp = &pf->dplls.pps;
>>> +	struct ice_dplls *d = &pf->dplls;
>>> +	struct ice_hw *hw = &pf->hw;
>>> +	int ret, alloc_size, i;
>>> +
>>> +	d->clock_id = ice_generate_clock_id(pf);
>>> +	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>> +	if (ret) {
>>> +		dev_err(ice_pf_to_dev(pf),
>>> +			"err:%d %s failed to read cgu abilities\n",
>>> +			ret, ice_aq_str(hw->adminq.sq_last_status));
>>> +		return ret;
>>> +	}
>>> +
>>> +	de->dpll_idx = abilities.eec_dpll_idx;
>>> +	dp->dpll_idx = abilities.pps_dpll_idx;
>>> +	d->num_inputs = abilities.num_inputs;
>>> +	d->num_outputs = abilities.num_outputs;
>>> +	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>>> +	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>>> +
>>> +	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>> +	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!d->inputs)
>>> +		return -ENOMEM;
>>> +
>>> +	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>> +	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!de->input_prio)
>>> +		return -ENOMEM;
>>> +
>>> +	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!dp->input_prio)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>>> +	if (ret)
>>> +		goto deinit_info;
>>> +
>>> +	if (cgu) {
>>> +		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>> +		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>> +		if (!d->outputs)
>>
>> Should ret be set to -ENOMEM here?
>>
>> Flagged by Smatch.
>>
>>> +			goto deinit_info;
>>> +
>>> +		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +		if (ret)
>>> +			goto deinit_info;
>>> +	}
>>> +
>>> +	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>>> +					&pf->dplls.rclk.num_parents);
>>> +	if (ret)
>>> +		return ret;
>>> +	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>> +		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>> +	if (ret)
>>> +		return ret;
>>> +	de->mode = DPLL_MODE_AUTOMATIC;
>>> +	dp->mode = DPLL_MODE_AUTOMATIC;
>>> +
>>> +	dev_dbg(ice_pf_to_dev(pf),
>>> +		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>>> +		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>>> +
>>> +	return 0;
>>> +
>>> +deinit_info:
>>> +	dev_err(ice_pf_to_dev(pf),
>>> +		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>>d->outputs:%p\n",
>>> +		__func__, d->inputs, de->input_prio,
>>> +		dp->input_prio, d->outputs);
>>> +	ice_dpll_deinit_info(pf);
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_init - initialize support for dpll subsystem
>>> + * @pf: board private structure
>>> + *
>>> + * Set up the device dplls, register them and pins connected within
>>>Linux dpll
>>> + * subsystem. Allow userpsace to obtain state of DPLL and handling of
>>>DPLL
>>
>> nit: userpsace -> userspace
>>
>>> + * configuration requests.
>>> + *
>>> + * Context: Function initializes and holds pf->dplls.lock mutex.
>>> + */
>>
>> ...
>>
>>> 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..975066b71c5e
>>> --- /dev/null
>>> +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>> @@ -0,0 +1,104 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/* Copyright (C) 2022, Intel Corporation. */
>>> +
>>> +#ifndef _ICE_DPLL_H_
>>> +#define _ICE_DPLL_H_
>>> +
>>> +#include "ice.h"
>>> +
>>> +#define ICE_DPLL_PRIO_MAX	0xF
>>> +#define ICE_DPLL_RCLK_NUM_MAX	4
>>> +
>>> +/** ice_dpll_pin - store info about pins
>>> + * @pin: dpll pin structure
>>> + * @pf: pointer to pf, which has registered the dpll_pin
>>> + * @idx: ice pin private idx
>>> + * @num_parents: hols number of parent pins
>>> + * @parent_idx: hold indexes of parent pins
>>> + * @flags: pin flags returned from HW
>>> + * @state: state of a pin
>>> + * @prop: pin properities
>>
>> nit: properities -> properties
>>
>>> + * @freq: current frequency of a pin
>>> + */
>>> +struct ice_dpll_pin {
>>> +	struct dpll_pin *pin;
>>> +	struct ice_pf *pf;
>>> +	u8 idx;
>>> +	u8 num_parents;
>>> +	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>>> +	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>>> +	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>>> +	struct dpll_pin_properties prop;
>>> +	u32 freq;
>>> +};
>>
>> ...


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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-28 23:10         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-07-28 23:10 UTC (permalink / raw)
  To: Vadim Fedorenko, Simon Horman
  Cc: Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni, Olech,
	Milena, Michalik, Michal, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche


>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>Sent: Monday, July 24, 2023 7:59 PM
>
>On 24.07.2023 18:41, Simon Horman wrote:
>> On Thu, Jul 20, 2023 at 10:19:01AM +0100, Vadim Fedorenko wrote:
>>
>> ...
>>
>> Hi Vadim,
>>
>
>Hi Simon!
>Thanks for the review. I believe Arkadiusz as the author of the patch will
>adjust the code accordingly
>

Yes, will fix all the findings, thank you Simon for pointing them out!
Arkadiusz

>>> +/**
>>> + * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>>> + * @pf: private board structure
>>> + *
>>> + * Unlock the mutex from the callback operations invoked by dpll
>>>subsystem.
>>> + */
>>> +static void ice_dpll_cb_unlock(struct ice_pf *pf)
>>> +{
>>> +	mutex_unlock(&pf->dplls.lock);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_freq_set - set pin's frequency
>>> + * @pf: private board structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being configured
>>> + * @freq: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Set requested frequency on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error on AQ or wrong pin type given
>>> + */
>>> +static int
>>> +ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>> +		      enum ice_dpll_pin_type pin_type, const u32 freq,
>>> +		      struct netlink_ext_ack *extack)
>>> +{
>>> +	int ret;
>>> +	u8 flags;
>>
>> Please arrange local variable declarations for new Networking
>> code in reverse xmas tree order - longest line to shortest.
>>
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>>> +		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>>> +					       pin->flags[0], freq, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>> +		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>> +						0, freq, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret) {
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to set pin freq:%u on
>>>pin:%u\n",
>>> +				   ret,
>>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				   freq, pin->idx);
>>> +		return ret;
>>> +	}
>>> +	pin->freq = freq;
>>> +
>>> +	return 0;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_pin_state_update - update pin's state
>>> + * @pf: private board struct
>>> + * @pin: structure with pin attributes to be updated
>>> + * @pin_type: type of pin being updated
>>> + * @extack: error reporting
>>> + *
>>> + * Determine pin current state and frequency, then update struct
>>> + * holding the pin info. For input pin states are separated for each
>>> + * dpll, for rclk pins states are separated for each parent.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +int
>>> +ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>> +			  enum ice_dpll_pin_type pin_type,
>>> +			  struct netlink_ext_ack *extack)
>>
>>> +/**
>>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       const u32 frequency,
>>> +		       struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_set - output pin callback for set
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	*frequency = p->freq;
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a input pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_get - output pin callback for get
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_enable - enable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being enabled
>>> + * @extack: error reporting
>>> + *
>>> + * Enable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		    enum ice_dpll_pin_type pin_type,
>>> +		    struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to enable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_disable - disable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being disabled
>>> + * @extack: error reporting
>>> + *
>>> + * Disable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		     enum ice_dpll_pin_type pin_type,
>>> +		     struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to disable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>
>>> +/**
>>> + * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       const u32 frequency,
>>> +		       struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_set - input pin callback for set frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_set - output pin callback for set
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: frequency to be set
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal set frequency command on a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't set in hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_set(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + * @pin_type: type of pin being configured
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Acquires pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>> +		       const struct dpll_device *dpll, void *dpll_priv,
>>> +		       u64 *frequency, struct netlink_ext_ack *extack,
>>> +		       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll_pin *p = pin_priv;
>>> +	struct ice_dpll *d = dpll_priv;
>>> +	struct ice_pf *pf = d->pf;
>>> +	int ret;
>>> +
>>> +	ret = ice_dpll_cb_lock(pf, extack);
>>> +	if (ret)
>>> +		return ret;
>>> +	*frequency = p->freq;
>>> +	ice_dpll_cb_unlock(pf);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_input_frequency_get - input pin callback for get frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a input pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_input_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			     const struct dpll_device *dpll, void *dpll_priv,
>>> +			     u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_INPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_output_frequency_get - output pin callback for get
>>>frequency
>>> + * @pin: pointer to a pin
>>> + * @pin_priv: private data pointer passed on pin registration
>>> + * @dpll: pointer to dpll
>>> + * @dpll_priv: private data pointer passed on dpll registration
>>> + * @frequency: on success holds pin's frequency
>>> + * @extack: error reporting
>>> + *
>>> + * Wraps internal get frequency command of a pin.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - error pin not found or couldn't get from hw
>>> + */
>>> +static int
>>> +ice_dpll_output_frequency_get(const struct dpll_pin *pin, void
>>>*pin_priv,
>>> +			      const struct dpll_device *dpll, void *dpll_priv,
>>> +			      u64 *frequency, struct netlink_ext_ack *extack)
>>> +{
>>> +	return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>>frequency,
>>> +				      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_enable - enable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being enabled
>>> + * @extack: error reporting
>>> + *
>>> + * Enable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		    enum ice_dpll_pin_type pin_type,
>>> +		    struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to enable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * ice_dpll_pin_disable - disable a pin on dplls
>>> + * @hw: board private hw structure
>>> + * @pin: pointer to a pin
>>> + * @pin_type: type of pin being disabled
>>> + * @extack: error reporting
>>> + *
>>> + * Disable a pin on both dplls. Store current state in pin->flags.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - OK
>>> + * * negative - error
>>> + */
>>> +static int
>>> +ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>> +		     enum ice_dpll_pin_type pin_type,
>>> +		     struct netlink_ext_ack *extack)
>>> +{
>>> +	u8 flags = 0;
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>> +		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>> +			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>> +		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	if (ret)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to disable %s pin:%u\n",
>>> +				   ret, ice_aq_str(hw->adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +
>>> +	return ret;
>>> +}
>>
>> Should this function be static?
>>
>>> +{
>>> +	int ret;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>>> +					       NULL, &pin->flags[0],
>>> +					       &pin->freq, NULL);
>>> +		if (ret)
>>> +			goto err;
>>> +		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>>> +			if (pin->pin) {
>>> +				pin->state[pf->dplls.eec.dpll_idx] =
>>> +					pin->pin == pf->dplls.eec.active_input ?
>>> +					DPLL_PIN_STATE_CONNECTED :
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +				pin->state[pf->dplls.pps.dpll_idx] =
>>> +					pin->pin == pf->dplls.pps.active_input ?
>>> +					DPLL_PIN_STATE_CONNECTED :
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +			} else {
>>> +				pin->state[pf->dplls.eec.dpll_idx] =
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +				pin->state[pf->dplls.pps.dpll_idx] =
>>> +					DPLL_PIN_STATE_SELECTABLE;
>>> +			}
>>> +		} else {
>>> +			pin->state[pf->dplls.eec.dpll_idx] =
>>> +				DPLL_PIN_STATE_DISCONNECTED;
>>> +			pin->state[pf->dplls.pps.dpll_idx] =
>>> +				DPLL_PIN_STATE_DISCONNECTED;
>>> +		}
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>>> +						&pin->flags[0], NULL,
>>> +						&pin->freq, NULL);
>>> +		if (ret)
>>> +			goto err;
>>> +		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>>> +			pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>>> +		else
>>> +			pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>
>> clang-16 complains that:
>>
>>    drivers/net/ethernet/intel/ice/ice_dpll.c:461:3: error: expected
>>expression
>>                    u8 parent, port_num =
>>ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>>
>> Which, I think means, it wants this case to be enclosed in { }
>>
>>> +		u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>>> +
>>> +		for (parent = 0; parent < pf->dplls.rclk.num_parents;
>>> +		     parent++) {
>>> +			u8 p = parent;
>>> +
>>> +			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
>>> +							 &port_num,
>>> +							 &pin->flags[parent],
>>> +							 NULL);
>>> +			if (ret)
>>> +				goto err;
>>> +			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>>> +			    pin->flags[parent])
>>> +				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>>> +			else
>>> +				pin->state[parent] =
>>> +					DPLL_PIN_STATE_DISCONNECTED;
>>> +		}
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return 0;
>>> +err:
>>> +	if (extack)
>>> +		NL_SET_ERR_MSG_FMT(extack,
>>> +				   "err:%d %s failed to update %s pin:%u\n",
>>> +				   ret,
>>> +				   ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				   pin_type_name[pin_type], pin->idx);
>>> +	else
>>> +		dev_err_ratelimited(ice_pf_to_dev(pf),
>>> +				    "err:%d %s failed to update %s pin:%u\n",
>>> +				    ret,
>>> +				    ice_aq_str(pf->hw.adminq.sq_last_status),
>>> +				    pin_type_name[pin_type], pin->idx);
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_update_state - update dpll state
>>> + * @pf: pf private structure
>>> + * @d: pointer to queried dpll device
>>> + * @init: if function called on initialization of ice dpll
>>> + *
>>> + * Poll current state of dpll from hw and update ice_dpll struct.
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - AQ failure
>>> + */
>>> +static int
>>> +ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>>> +{
>>> +	struct ice_dpll_pin *p = NULL;
>>> +	int ret;
>>> +
>>> +	ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>>> +				&d->input_idx, &d->ref_state, &d->eec_mode,
>>> +				&d->phase_shift, &d->dpll_state, &d->mode);
>>> +
>>> +	dev_dbg(ice_pf_to_dev(pf),
>>> +		"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d
>>>mode:%d\n",
>>> +		d->dpll_idx, d->prev_input_idx, d->input_idx,
>>> +		d->dpll_state, d->prev_dpll_state, d->mode);
>>> +	if (ret) {
>>> +		dev_err(ice_pf_to_dev(pf),
>>> +			"update dpll=%d state failed, ret=%d %s\n",
>>> +			d->dpll_idx, ret,
>>> +			ice_aq_str(pf->hw.adminq.sq_last_status));
>>> +		return ret;
>>> +	}
>>> +	if (init) {
>>> +		if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED &&
>>> +		    d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
>>
>> Should this be '||' rather than '&&' ?
>>
>> Flagged by a clang-16 W=1 build, Sparse and Smatch.
>>
>>> +			d->active_input = pf->dplls.inputs[d->input_idx].pin;
>>> +		p = &pf->dplls.inputs[d->input_idx];
>>> +		return ice_dpll_pin_state_update(pf, p,
>>> +						 ICE_DPLL_PIN_TYPE_INPUT, NULL);
>>> +	}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_init_info_direct_pins - initializes direct pins info
>>> + * @pf: board private structure
>>> + * @pin_type: type of pins being initialized
>>> + *
>>> + * Init information for directly connected pins, cache them in pf's
>>>pins
>>> + * structures.
>>> + *
>>> + * Context: Called under pf->dplls.lock.
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - init failure reason
>>> + */
>>> +static int
>>> +ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>> +			       enum ice_dpll_pin_type pin_type)
>>> +{
>>> +	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>> +	struct ice_hw *hw = &pf->hw;
>>> +	struct ice_dpll_pin *pins;
>>> +	int num_pins, i, ret;
>>> +	u8 freq_supp_num;
>>> +	bool input;
>>> +
>>> +	switch (pin_type) {
>>> +	case ICE_DPLL_PIN_TYPE_INPUT:
>>> +		pins = pf->dplls.inputs;
>>> +		num_pins = pf->dplls.num_inputs;
>>> +		input = true;
>>> +		break;
>>> +	case ICE_DPLL_PIN_TYPE_OUTPUT:
>>> +		pins = pf->dplls.outputs;
>>> +		num_pins = pf->dplls.num_outputs;
>>> +		input = false;
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	for (i = 0; i < num_pins; i++) {
>>> +		pins[i].idx = i;
>>> +		pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>> +		pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>> +		if (input) {
>>> +			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>> +						      &de->input_prio[i]);
>>> +			if (ret)
>>> +				return ret;
>>> +			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>> +						      &dp->input_prio[i]);
>>> +			if (ret)
>>> +				return ret;
>>> +			pins[i].prop.capabilities |=
>>> +				DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>> +		}
>>> +		pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>> +		ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
>>> +		if (ret)
>>> +			return ret;
>>> +		pins[i].prop.freq_supported =
>>> +			ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>> +		pins[i].prop.freq_supported_num = freq_supp_num;
>>> +		pins[i].pf = pf;
>>> +	}
>>> +
>>
>> I'm unsure if this can happen,
>> but if the for loop above iterates zero times
>> then ret will be null here.
>>
>> Use of uninitialised variable flagged by Smatch.
>>
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * 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).
>>> + *
>>> + * Context: Called under pf->dplls.lock
>>> + * Return:
>>> + * * 0 - success
>>> + * * negative - init failure reason
>>> + */
>>> +static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>>> +{
>>> +	struct ice_aqc_get_cgu_abilities abilities;
>>> +	struct ice_dpll *de = &pf->dplls.eec;
>>> +	struct ice_dpll *dp = &pf->dplls.pps;
>>> +	struct ice_dplls *d = &pf->dplls;
>>> +	struct ice_hw *hw = &pf->hw;
>>> +	int ret, alloc_size, i;
>>> +
>>> +	d->clock_id = ice_generate_clock_id(pf);
>>> +	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>> +	if (ret) {
>>> +		dev_err(ice_pf_to_dev(pf),
>>> +			"err:%d %s failed to read cgu abilities\n",
>>> +			ret, ice_aq_str(hw->adminq.sq_last_status));
>>> +		return ret;
>>> +	}
>>> +
>>> +	de->dpll_idx = abilities.eec_dpll_idx;
>>> +	dp->dpll_idx = abilities.pps_dpll_idx;
>>> +	d->num_inputs = abilities.num_inputs;
>>> +	d->num_outputs = abilities.num_outputs;
>>> +	d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj);
>>> +	d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj);
>>> +
>>> +	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>> +	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!d->inputs)
>>> +		return -ENOMEM;
>>> +
>>> +	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>> +	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!de->input_prio)
>>> +		return -ENOMEM;
>>> +
>>> +	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>> +	if (!dp->input_prio)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>>> +	if (ret)
>>> +		goto deinit_info;
>>> +
>>> +	if (cgu) {
>>> +		alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>> +		d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>> +		if (!d->outputs)
>>
>> Should ret be set to -ENOMEM here?
>>
>> Flagged by Smatch.
>>
>>> +			goto deinit_info;
>>> +
>>> +		ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>> +		if (ret)
>>> +			goto deinit_info;
>>> +	}
>>> +
>>> +	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>>> +					&pf->dplls.rclk.num_parents);
>>> +	if (ret)
>>> +		return ret;
>>> +	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>> +		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>>> +	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>> +	if (ret)
>>> +		return ret;
>>> +	de->mode = DPLL_MODE_AUTOMATIC;
>>> +	dp->mode = DPLL_MODE_AUTOMATIC;
>>> +
>>> +	dev_dbg(ice_pf_to_dev(pf),
>>> +		"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>>> +		__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>>> +
>>> +	return 0;
>>> +
>>> +deinit_info:
>>> +	dev_err(ice_pf_to_dev(pf),
>>> +		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>>d->outputs:%p\n",
>>> +		__func__, d->inputs, de->input_prio,
>>> +		dp->input_prio, d->outputs);
>>> +	ice_dpll_deinit_info(pf);
>>> +	return ret;
>>> +}
>>
>> ...
>>
>>> +/**
>>> + * ice_dpll_init - initialize support for dpll subsystem
>>> + * @pf: board private structure
>>> + *
>>> + * Set up the device dplls, register them and pins connected within
>>>Linux dpll
>>> + * subsystem. Allow userpsace to obtain state of DPLL and handling of
>>>DPLL
>>
>> nit: userpsace -> userspace
>>
>>> + * configuration requests.
>>> + *
>>> + * Context: Function initializes and holds pf->dplls.lock mutex.
>>> + */
>>
>> ...
>>
>>> 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..975066b71c5e
>>> --- /dev/null
>>> +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>> @@ -0,0 +1,104 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/* Copyright (C) 2022, Intel Corporation. */
>>> +
>>> +#ifndef _ICE_DPLL_H_
>>> +#define _ICE_DPLL_H_
>>> +
>>> +#include "ice.h"
>>> +
>>> +#define ICE_DPLL_PRIO_MAX	0xF
>>> +#define ICE_DPLL_RCLK_NUM_MAX	4
>>> +
>>> +/** ice_dpll_pin - store info about pins
>>> + * @pin: dpll pin structure
>>> + * @pf: pointer to pf, which has registered the dpll_pin
>>> + * @idx: ice pin private idx
>>> + * @num_parents: hols number of parent pins
>>> + * @parent_idx: hold indexes of parent pins
>>> + * @flags: pin flags returned from HW
>>> + * @state: state of a pin
>>> + * @prop: pin properities
>>
>> nit: properities -> properties
>>
>>> + * @freq: current frequency of a pin
>>> + */
>>> +struct ice_dpll_pin {
>>> +	struct dpll_pin *pin;
>>> +	struct ice_pf *pf;
>>> +	u8 idx;
>>> +	u8 num_parents;
>>> +	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>>> +	u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>>> +	u8 state[ICE_DPLL_RCLK_NUM_MAX];
>>> +	struct dpll_pin_properties prop;
>>> +	u32 freq;
>>> +};
>>
>> ...

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-26 15:20               ` Paolo Abeni
@ 2023-07-31 12:08                   ` Jiri Pirko
  2023-07-31 12:08                   ` Jiri Pirko
  1 sibling, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:08 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: Jakub Kicinski, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Jonathan Lemon, Olech, Milena, Michalik, Michal,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

Wed, Jul 26, 2023 at 05:20:12PM CEST, pabeni@redhat.com wrote:
>On Tue, 2023-07-25 at 15:49 -0700, Jakub Kicinski wrote:
>> On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> > So it is not a mode! Mode is either "automatic" or "manual". Then we
>> > have a state to indicate the state of the state machine (unlocked, locked,
>> > holdover, holdover-acq). So what you seek is a way for the user to
>> > expliticly set the state to "unlocked" and reset of the state machine.
>> 
>> +1 for mixing the state machine and config.
>> Maybe a compromise would be to rename the config mode?
>> Detached? Standalone?
>
>For the records, I don't know the H/W details to any extents, but
>generally speaking it sounds reasonable to me that a mode change could
>cause a state change.

The thing is, you don't need an extra mode just to "reset state". There
could be a command for it, staying under the same mode. That way, things
would be cleaner and obvious for the user.
case a)
AUTOMATIC MODE
user changes to FREERUN to reset state
user changes back to AUTOMATIC to continue

case b)
AUTOMATIC MODE
user submits state reset command


>
>e.g. switching an ethernet device autoneg mode could cause the link
>state to flip.
>
>So I'm ok with the existence of the freerun mode.
>
>I think it should be clarified what happens if pins are manually
>enabled in such mode. I expect ~nothing will change, but stating it

That is another very good point you touched. In the "freerun"
mode, the pins does not have any meaning.
The same you achieve with automatic mode, setting all pins to
disconnect.

If we have freerun mode, the core should sanitize all pins are
disconnect and stay disconnect. But do you see how ridiculous this is
becoming? :)


>clearly would help.
>
>Cheers,
>
>Paolo
>

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-31 12:08                   ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:08 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: Jakub Kicinski, Kubalewski, Arkadiusz, Vadim Fedorenko,
	Jonathan Lemon, Olech, Milena, Michalik, Michal,
	linux-arm-kernel, poros, mschmidt, netdev, linux-clk,
	Bart Van Assche

Wed, Jul 26, 2023 at 05:20:12PM CEST, pabeni@redhat.com wrote:
>On Tue, 2023-07-25 at 15:49 -0700, Jakub Kicinski wrote:
>> On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> > So it is not a mode! Mode is either "automatic" or "manual". Then we
>> > have a state to indicate the state of the state machine (unlocked, locked,
>> > holdover, holdover-acq). So what you seek is a way for the user to
>> > expliticly set the state to "unlocked" and reset of the state machine.
>> 
>> +1 for mixing the state machine and config.
>> Maybe a compromise would be to rename the config mode?
>> Detached? Standalone?
>
>For the records, I don't know the H/W details to any extents, but
>generally speaking it sounds reasonable to me that a mode change could
>cause a state change.

The thing is, you don't need an extra mode just to "reset state". There
could be a command for it, staying under the same mode. That way, things
would be cleaner and obvious for the user.
case a)
AUTOMATIC MODE
user changes to FREERUN to reset state
user changes back to AUTOMATIC to continue

case b)
AUTOMATIC MODE
user submits state reset command


>
>e.g. switching an ethernet device autoneg mode could cause the link
>state to flip.
>
>So I'm ok with the existence of the freerun mode.
>
>I think it should be clarified what happens if pins are manually
>enabled in such mode. I expect ~nothing will change, but stating it

That is another very good point you touched. In the "freerun"
mode, the pins does not have any meaning.
The same you achieve with automatic mode, setting all pins to
disconnect.

If we have freerun mode, the core should sanitize all pins are
disconnect and stay disconnect. But do you see how ridiculous this is
becoming? :)


>clearly would help.
>
>Cheers,
>
>Paolo
>

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

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-25 22:49             ` Jakub Kicinski
@ 2023-07-31 12:11                 ` Jiri Pirko
  2023-07-26 21:08               ` Kubalewski, Arkadiusz
  2023-07-31 12:11                 ` Jiri Pirko
  2 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:11 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Wed, Jul 26, 2023 at 12:49:58AM CEST, kuba@kernel.org wrote:
>On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> So it is not a mode! Mode is either "automatic" or "manual". Then we
>> have a state to indicate the state of the state machine (unlocked, locked,
>> holdover, holdover-acq). So what you seek is a way for the user to
>> expliticly set the state to "unlocked" and reset of the state machine.
>
>+1 for mixing the state machine and config.
>Maybe a compromise would be to rename the config mode?
>Detached? Standalone?

But even with different name, you will still have the same mixture.

Why having automatic/manual mode with possibility to connect/disconnect
pins with additional uapi extension to submit state reset command is
not enough? Clear and simple, easy to document and understand.

There are too many uncertanties about "freerun" mode, very confusing,
not clear behaviour (as this thread demonstrated). That is extually very
good reason to don't have it. Could we please drop it?


>
>> Please don't mix config and state. I think we untangled this in the past
>> :/
>> 
>> Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>> to hit this button.

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-31 12:11                 ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:11 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, Olech, Milena, Michalik, Michal, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Wed, Jul 26, 2023 at 12:49:58AM CEST, kuba@kernel.org wrote:
>On Fri, 21 Jul 2023 14:02:08 +0200 Jiri Pirko wrote:
>> So it is not a mode! Mode is either "automatic" or "manual". Then we
>> have a state to indicate the state of the state machine (unlocked, locked,
>> holdover, holdover-acq). So what you seek is a way for the user to
>> expliticly set the state to "unlocked" and reset of the state machine.
>
>+1 for mixing the state machine and config.
>Maybe a compromise would be to rename the config mode?
>Detached? Standalone?

But even with different name, you will still have the same mixture.

Why having automatic/manual mode with possibility to connect/disconnect
pins with additional uapi extension to submit state reset command is
not enough? Clear and simple, easy to document and understand.

There are too many uncertanties about "freerun" mode, very confusing,
not clear behaviour (as this thread demonstrated). That is extually very
good reason to don't have it. Could we please drop it?


>
>> Please don't mix config and state. I think we untangled this in the past
>> :/
>> 
>> Perhaps you just need an extra cmd like DPLL_CMD_DEVICE_STATE_RESET cmd
>> to hit this button.

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-28 23:03       ` Kubalewski, Arkadiusz
@ 2023-07-31 12:19         ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:19 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 1:39 PM
>>
>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>

[...]


>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>*extack)
>>>+{
>>>+	int i;
>>>+
>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>
>>And again, as I already told you, this flag checking is totally
>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>
>
>This is not pointless, will explain below.
>
>>
>>
>
>[...]
>

[...]


>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>+{
>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>+
>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>+		return;
>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>+
>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>+	ice_dpll_deinit_info(pf);
>>>+	if (cgu)
>>>+		ice_dpll_deinit_worker(pf);
>>
>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>ice_dpll_periodic_work() function is the only reason why you need it
>>currently.
>>
>
>Not true.
>The feature flag is common approach in ice. If the feature was successfully

The fact that something is common does not necessarily mean it is
correct. 0 value argument.


>initialized the flag is set. It allows to determine if deinit of the feature
>is required on driver unload.
>
>Right now the check for the flag is not only in kworker but also in each
>callback, if the flag were cleared the data shall be not accessed by callbacks.

Could you please draw me a scenario when this could actually happen?
It is just a matter of ordering. Unregister dpll device/pins before you
cleanup the related resources and you don't need this ridiculous flag.


>I know this is not required, but it helps on loading and unloading the driver,
>thanks to that, spam of pin-get dump is not slowing the driver load/unload.

? Could you plese draw me a scenario how such thing may actually happen?

Thanks!


>
>>
>>>+	mutex_destroy(&pf->dplls.lock);
>>>+}


[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-07-31 12:19         ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-07-31 12:19 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, July 21, 2023 1:39 PM
>>
>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>

[...]


>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>*extack)
>>>+{
>>>+	int i;
>>>+
>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>
>>And again, as I already told you, this flag checking is totally
>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>
>
>This is not pointless, will explain below.
>
>>
>>
>
>[...]
>

[...]


>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>+{
>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>+
>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>+		return;
>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>+
>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>+	ice_dpll_deinit_info(pf);
>>>+	if (cgu)
>>>+		ice_dpll_deinit_worker(pf);
>>
>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>ice_dpll_periodic_work() function is the only reason why you need it
>>currently.
>>
>
>Not true.
>The feature flag is common approach in ice. If the feature was successfully

The fact that something is common does not necessarily mean it is
correct. 0 value argument.


>initialized the flag is set. It allows to determine if deinit of the feature
>is required on driver unload.
>
>Right now the check for the flag is not only in kworker but also in each
>callback, if the flag were cleared the data shall be not accessed by callbacks.

Could you please draw me a scenario when this could actually happen?
It is just a matter of ordering. Unregister dpll device/pins before you
cleanup the related resources and you don't need this ridiculous flag.


>I know this is not required, but it helps on loading and unloading the driver,
>thanks to that, spam of pin-get dump is not slowing the driver load/unload.

? Could you plese draw me a scenario how such thing may actually happen?

Thanks!


>
>>
>>>+	mutex_destroy(&pf->dplls.lock);
>>>+}


[...]

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

* Re: [PATCH net-next 06/11] dpll: netlink: Add DPLL framework base functions
  2023-07-20  9:18   ` Vadim Fedorenko
@ 2023-08-01 12:23     ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-08-01 12:23 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:58AM CEST, vadim.fedorenko@linux.dev wrote:

[...]

>+
>+static int
>+dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>+			   struct netlink_ext_ack *extack)
>+{
>+	struct nlattr *tb[DPLL_A_MAX + 1];
>+	enum dpll_pin_direction direction;
>+	enum dpll_pin_state state;
>+	struct dpll_pin_ref *ref;
>+	struct dpll_device *dpll;
>+	u32 pdpll_idx, prio;
>+	int ret;
>+
>+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>+			 NULL, extack);

Please pass proper policy instead of NULL here.
dpll_pin_parent_device_nl_policy


>+	if (!tb[DPLL_A_ID]) {
>+		NL_SET_ERR_MSG(extack, "device parent id expected");
>+		return -EINVAL;
>+	}
>+	pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
>+	dpll = xa_load(&dpll_device_xa, pdpll_idx);
>+	if (!dpll)
>+		return -EINVAL;
>+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+	ASSERT_NOT_NULL(ref);
>+	if (tb[DPLL_A_PIN_STATE]) {
>+		state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+		ret = dpll_pin_state_set(dpll, pin, state, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (tb[DPLL_A_PIN_PRIO]) {
>+		prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
>+		ret = dpll_pin_prio_set(dpll, pin, prio, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (tb[DPLL_A_PIN_DIRECTION]) {
>+		direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
>+		ret = dpll_pin_direction_set(pin, dpll, direction, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>+			struct netlink_ext_ack *extack)
>+{
>+	struct nlattr *tb[DPLL_A_MAX + 1];
>+	enum dpll_pin_state state;
>+	u32 ppin_idx;
>+	int ret;
>+
>+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>+			 NULL, extack);

Please pass proper policy instead of NULL here.
dpll_pin_parent_pin_nl_policy


>+	if (!tb[DPLL_A_PIN_ID]) {
>+		NL_SET_ERR_MSG(extack, "parent pin id expected");
>+		return -EINVAL;
>+	}
>+	ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
>+	state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+	ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
>+	if (ret)
>+		return ret;
>+
>+	return 0;
>+}

[...]

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

* Re: [PATCH net-next 06/11] dpll: netlink: Add DPLL framework base functions
@ 2023-08-01 12:23     ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-08-01 12:23 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, Milena Olech, Michal Michalik, linux-arm-kernel,
	poros, mschmidt, netdev, linux-clk, Bart Van Assche

Thu, Jul 20, 2023 at 11:18:58AM CEST, vadim.fedorenko@linux.dev wrote:

[...]

>+
>+static int
>+dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>+			   struct netlink_ext_ack *extack)
>+{
>+	struct nlattr *tb[DPLL_A_MAX + 1];
>+	enum dpll_pin_direction direction;
>+	enum dpll_pin_state state;
>+	struct dpll_pin_ref *ref;
>+	struct dpll_device *dpll;
>+	u32 pdpll_idx, prio;
>+	int ret;
>+
>+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>+			 NULL, extack);

Please pass proper policy instead of NULL here.
dpll_pin_parent_device_nl_policy


>+	if (!tb[DPLL_A_ID]) {
>+		NL_SET_ERR_MSG(extack, "device parent id expected");
>+		return -EINVAL;
>+	}
>+	pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
>+	dpll = xa_load(&dpll_device_xa, pdpll_idx);
>+	if (!dpll)
>+		return -EINVAL;
>+	ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+	ASSERT_NOT_NULL(ref);
>+	if (tb[DPLL_A_PIN_STATE]) {
>+		state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+		ret = dpll_pin_state_set(dpll, pin, state, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (tb[DPLL_A_PIN_PRIO]) {
>+		prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
>+		ret = dpll_pin_prio_set(dpll, pin, prio, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	if (tb[DPLL_A_PIN_DIRECTION]) {
>+		direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
>+		ret = dpll_pin_direction_set(pin, dpll, direction, extack);
>+		if (ret)
>+			return ret;
>+	}
>+	return 0;
>+}
>+
>+static int
>+dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>+			struct netlink_ext_ack *extack)
>+{
>+	struct nlattr *tb[DPLL_A_MAX + 1];
>+	enum dpll_pin_state state;
>+	u32 ppin_idx;
>+	int ret;
>+
>+	nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>+			 NULL, extack);

Please pass proper policy instead of NULL here.
dpll_pin_parent_pin_nl_policy


>+	if (!tb[DPLL_A_PIN_ID]) {
>+		NL_SET_ERR_MSG(extack, "parent pin id expected");
>+		return -EINVAL;
>+	}
>+	ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
>+	state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+	ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
>+	if (ret)
>+		return ret;
>+
>+	return 0;
>+}

[...]

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

* Re: [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
  2023-07-20  9:18   ` Vadim Fedorenko
@ 2023-08-01 13:23     ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-08-01 13:23 UTC (permalink / raw)
  To: Jiri Pirko, Jiri Pirko
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche, Jakub Kicinski,
	Arkadiusz Kubalewski, Paolo Abeni, Jonathan Lemon

On 20/07/2023 10:18, Vadim Fedorenko wrote:
> From: Jiri Pirko <jiri@nvidia.com>
> 
> In case netdevice represents a SyncE port, the user needs to understand
> the connection between netdevice and associated DPLL pin. There might me
> multiple netdevices pointing to the same pin, in case of VF/SF
> implementation.
> 
> Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
> how it is implemented for devlink port. Add a struct dpll_pin pointer
> to netdev and protect access to it by RTNL. Expose netdev_dpll_pin_set()
> and netdev_dpll_pin_clear() helpers to the drivers so they can set/clear
> the DPLL pin relationship to netdev.
> 
> Note that during the lifetime of struct dpll_pin the pin handle does not
> change. Therefore it is save to access it lockless. It is drivers
> responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().
> 
> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> ---
> RFC v9->v0:
> - rearrange function definition according to usage
> v8->v9:
> - net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
>    code in net/core/rtnetlink.c to respect that.
> - move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
>    function with this patch
> 
>   drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
>   include/linux/dpll.h         | 20 ++++++++++++++++++++
>   include/linux/netdevice.h    | 20 ++++++++++++++++++++
>   include/uapi/linux/if_link.h |  2 ++
>   net/core/dev.c               | 22 ++++++++++++++++++++++
>   net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
>   6 files changed, 115 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
> index c44dda78737d..e4a9bd767b92 100644
> --- a/drivers/dpll/dpll_netlink.c
> +++ b/drivers/dpll/dpll_netlink.c
> @@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
>   	return 0;
>   }
>   
> +/**
> + * dpll_msg_pin_handle_size - get size of pin handle attribute for given pin
> + * @pin: pin pointer
> + *
> + * Return: byte size of pin handle attribute for given pin.
> + */
> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +	return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
> +}
> +EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
> +
>   /**
>    * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
>    * @msg: pointer to sk_buff message to attach a pin handle
> @@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
>   		return -EMSGSIZE;
>   	return 0;
>   }
> +EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);

Jiri, could you please remind me what is the reason to export this 
function? Because I cannot
any usage of this function in drivers.


[....]

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

* Re: [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
@ 2023-08-01 13:23     ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-08-01 13:23 UTC (permalink / raw)
  To: Jiri Pirko, Jiri Pirko
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche, Jakub Kicinski,
	Arkadiusz Kubalewski, Paolo Abeni, Jonathan Lemon

On 20/07/2023 10:18, Vadim Fedorenko wrote:
> From: Jiri Pirko <jiri@nvidia.com>
> 
> In case netdevice represents a SyncE port, the user needs to understand
> the connection between netdevice and associated DPLL pin. There might me
> multiple netdevices pointing to the same pin, in case of VF/SF
> implementation.
> 
> Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
> how it is implemented for devlink port. Add a struct dpll_pin pointer
> to netdev and protect access to it by RTNL. Expose netdev_dpll_pin_set()
> and netdev_dpll_pin_clear() helpers to the drivers so they can set/clear
> the DPLL pin relationship to netdev.
> 
> Note that during the lifetime of struct dpll_pin the pin handle does not
> change. Therefore it is save to access it lockless. It is drivers
> responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().
> 
> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> ---
> RFC v9->v0:
> - rearrange function definition according to usage
> v8->v9:
> - net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
>    code in net/core/rtnetlink.c to respect that.
> - move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
>    function with this patch
> 
>   drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
>   include/linux/dpll.h         | 20 ++++++++++++++++++++
>   include/linux/netdevice.h    | 20 ++++++++++++++++++++
>   include/uapi/linux/if_link.h |  2 ++
>   net/core/dev.c               | 22 ++++++++++++++++++++++
>   net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
>   6 files changed, 115 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
> index c44dda78737d..e4a9bd767b92 100644
> --- a/drivers/dpll/dpll_netlink.c
> +++ b/drivers/dpll/dpll_netlink.c
> @@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
>   	return 0;
>   }
>   
> +/**
> + * dpll_msg_pin_handle_size - get size of pin handle attribute for given pin
> + * @pin: pin pointer
> + *
> + * Return: byte size of pin handle attribute for given pin.
> + */
> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +	return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
> +}
> +EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
> +
>   /**
>    * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
>    * @msg: pointer to sk_buff message to attach a pin handle
> @@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
>   		return -EMSGSIZE;
>   	return 0;
>   }
> +EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);

Jiri, could you please remind me what is the reason to export this 
function? Because I cannot
any usage of this function in drivers.


[....]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-07-31 12:19         ` Jiri Pirko
@ 2023-08-01 14:50           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-01 14:50 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, July 31, 2023 2:20 PM
>
>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 1:39 PM
>>>
>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>
>[...]
>
>
>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>*extack)
>>>>+{
>>>>+	int i;
>>>>+
>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>
>>>And again, as I already told you, this flag checking is totally
>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>
>>
>>This is not pointless, will explain below.
>>
>>>
>>>
>>
>>[...]
>>
>
>[...]
>
>
>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>+{
>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>+
>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>+		return;
>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>+
>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>+	ice_dpll_deinit_info(pf);
>>>>+	if (cgu)
>>>>+		ice_dpll_deinit_worker(pf);
>>>
>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>currently.
>>>
>>
>>Not true.
>>The feature flag is common approach in ice. If the feature was successfully
>
>The fact that something is common does not necessarily mean it is
>correct. 0 value argument.
>

Like using functions that unwrap netlink attributes as unsigned when
they are in fact enums with possibility of being signed?

This is about consistent approach in ice driver.

>
>>initialized the flag is set. It allows to determine if deinit of the feature
>>is required on driver unload.
>>
>>Right now the check for the flag is not only in kworker but also in each
>>callback, if the flag were cleared the data shall be not accessed by
>>callbacks.
>
>Could you please draw me a scenario when this could actually happen?
>It is just a matter of ordering. Unregister dpll device/pins before you
>cleanup the related resources and you don't need this ridiculous flag.
>

Flag allows to determine if dpll was successfully initialized and do proper
deinit on rmmod only if it was initialized. That's all.

>
>>I know this is not required, but it helps on loading and unloading the
>>driver,
>>thanks to that, spam of pin-get dump is not slowing the driver
>>load/unload.
>
>? Could you plese draw me a scenario how such thing may actually happen?

First of all I said it is not required.

I already draw you this with above sentence.
You need spam pin-get asynchronously and unload driver, what is not clear?
Basically mutex in dpll is a bottleneck, with multiple requests waiting for
mutex there is low change of driver getting mutex when doing unregisters.

We actually need to redesign the mutex in dpll core/netlink, but I guess after
initial submission.

Thank you!
Arkadiusz

>
>Thanks!
>
>
>>
>>>
>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>+}
>
>
>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-08-01 14:50           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-01 14:50 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, July 31, 2023 2:20 PM
>
>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, July 21, 2023 1:39 PM
>>>
>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>
>[...]
>
>
>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>*extack)
>>>>+{
>>>>+	int i;
>>>>+
>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>
>>>And again, as I already told you, this flag checking is totally
>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>
>>
>>This is not pointless, will explain below.
>>
>>>
>>>
>>
>>[...]
>>
>
>[...]
>
>
>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>+{
>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>+
>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>+		return;
>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>+
>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>+	ice_dpll_deinit_info(pf);
>>>>+	if (cgu)
>>>>+		ice_dpll_deinit_worker(pf);
>>>
>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>currently.
>>>
>>
>>Not true.
>>The feature flag is common approach in ice. If the feature was successfully
>
>The fact that something is common does not necessarily mean it is
>correct. 0 value argument.
>

Like using functions that unwrap netlink attributes as unsigned when
they are in fact enums with possibility of being signed?

This is about consistent approach in ice driver.

>
>>initialized the flag is set. It allows to determine if deinit of the feature
>>is required on driver unload.
>>
>>Right now the check for the flag is not only in kworker but also in each
>>callback, if the flag were cleared the data shall be not accessed by
>>callbacks.
>
>Could you please draw me a scenario when this could actually happen?
>It is just a matter of ordering. Unregister dpll device/pins before you
>cleanup the related resources and you don't need this ridiculous flag.
>

Flag allows to determine if dpll was successfully initialized and do proper
deinit on rmmod only if it was initialized. That's all.

>
>>I know this is not required, but it helps on loading and unloading the
>>driver,
>>thanks to that, spam of pin-get dump is not slowing the driver
>>load/unload.
>
>? Could you plese draw me a scenario how such thing may actually happen?

First of all I said it is not required.

I already draw you this with above sentence.
You need spam pin-get asynchronously and unload driver, what is not clear?
Basically mutex in dpll is a bottleneck, with multiple requests waiting for
mutex there is low change of driver getting mutex when doing unregisters.

We actually need to redesign the mutex in dpll core/netlink, but I guess after
initial submission.

Thank you!
Arkadiusz

>
>Thanks!
>
>
>>
>>>
>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>+}
>
>
>[...]

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

* Re: [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
  2023-08-01 13:23     ` Vadim Fedorenko
@ 2023-08-01 15:12       ` Vadim Fedorenko
  -1 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-08-01 15:12 UTC (permalink / raw)
  To: Jiri Pirko, Jiri Pirko
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche, Jakub Kicinski,
	Arkadiusz Kubalewski, Paolo Abeni, Jonathan Lemon

On 01/08/2023 14:23, Vadim Fedorenko wrote:
> On 20/07/2023 10:18, Vadim Fedorenko wrote:
>> From: Jiri Pirko <jiri@nvidia.com>
>>
>> In case netdevice represents a SyncE port, the user needs to understand
>> the connection between netdevice and associated DPLL pin. There might me
>> multiple netdevices pointing to the same pin, in case of VF/SF
>> implementation.
>>
>> Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
>> how it is implemented for devlink port. Add a struct dpll_pin pointer
>> to netdev and protect access to it by RTNL. Expose netdev_dpll_pin_set()
>> and netdev_dpll_pin_clear() helpers to the drivers so they can set/clear
>> the DPLL pin relationship to netdev.
>>
>> Note that during the lifetime of struct dpll_pin the pin handle does not
>> change. Therefore it is save to access it lockless. It is drivers
>> responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().
>>
>> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> ---
>> RFC v9->v0:
>> - rearrange function definition according to usage
>> v8->v9:
>> - net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
>>    code in net/core/rtnetlink.c to respect that.
>> - move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
>>    function with this patch
>>
>>   drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
>>   include/linux/dpll.h         | 20 ++++++++++++++++++++
>>   include/linux/netdevice.h    | 20 ++++++++++++++++++++
>>   include/uapi/linux/if_link.h |  2 ++
>>   net/core/dev.c               | 22 ++++++++++++++++++++++
>>   net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
>>   6 files changed, 115 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>> index c44dda78737d..e4a9bd767b92 100644
>> --- a/drivers/dpll/dpll_netlink.c
>> +++ b/drivers/dpll/dpll_netlink.c
>> @@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct 
>> dpll_device *dpll)
>>       return 0;
>>   }
>> +/**
>> + * dpll_msg_pin_handle_size - get size of pin handle attribute for 
>> given pin
>> + * @pin: pin pointer
>> + *
>> + * Return: byte size of pin handle attribute for given pin.
>> + */
>> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
>> +{
>> +    return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
>> +
>>   /**
>>    * dpll_msg_add_pin_handle - attach pin handle attribute to a given 
>> message
>>    * @msg: pointer to sk_buff message to attach a pin handle
>> @@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, 
>> struct dpll_pin *pin)
>>           return -EMSGSIZE;
>>       return 0;
>>   }
>> +EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
> 
> Jiri, could you please remind me what is the reason to export this 
> function? Because I cannot
> any usage of this function in drivers.
> 

Ah, found it, sorry for the noise.

> 
> [....]


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

* Re: [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice
@ 2023-08-01 15:12       ` Vadim Fedorenko
  0 siblings, 0 replies; 109+ messages in thread
From: Vadim Fedorenko @ 2023-08-01 15:12 UTC (permalink / raw)
  To: Jiri Pirko, Jiri Pirko
  Cc: Milena Olech, Michal Michalik, linux-arm-kernel, poros, mschmidt,
	netdev, linux-clk, Bart Van Assche, Jakub Kicinski,
	Arkadiusz Kubalewski, Paolo Abeni, Jonathan Lemon

On 01/08/2023 14:23, Vadim Fedorenko wrote:
> On 20/07/2023 10:18, Vadim Fedorenko wrote:
>> From: Jiri Pirko <jiri@nvidia.com>
>>
>> In case netdevice represents a SyncE port, the user needs to understand
>> the connection between netdevice and associated DPLL pin. There might me
>> multiple netdevices pointing to the same pin, in case of VF/SF
>> implementation.
>>
>> Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
>> how it is implemented for devlink port. Add a struct dpll_pin pointer
>> to netdev and protect access to it by RTNL. Expose netdev_dpll_pin_set()
>> and netdev_dpll_pin_clear() helpers to the drivers so they can set/clear
>> the DPLL pin relationship to netdev.
>>
>> Note that during the lifetime of struct dpll_pin the pin handle does not
>> change. Therefore it is save to access it lockless. It is drivers
>> responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().
>>
>> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> ---
>> RFC v9->v0:
>> - rearrange function definition according to usage
>> v8->v9:
>> - net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL) fix the
>>    code in net/core/rtnetlink.c to respect that.
>> - move dpll_msg_add_pin_handle to "dpll: netlink" patch + export the
>>    function with this patch
>>
>>   drivers/dpll/dpll_netlink.c  | 19 ++++++++++++++++---
>>   include/linux/dpll.h         | 20 ++++++++++++++++++++
>>   include/linux/netdevice.h    | 20 ++++++++++++++++++++
>>   include/uapi/linux/if_link.h |  2 ++
>>   net/core/dev.c               | 22 ++++++++++++++++++++++
>>   net/core/rtnetlink.c         | 35 +++++++++++++++++++++++++++++++++++
>>   6 files changed, 115 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>> index c44dda78737d..e4a9bd767b92 100644
>> --- a/drivers/dpll/dpll_netlink.c
>> +++ b/drivers/dpll/dpll_netlink.c
>> @@ -37,6 +37,18 @@ dpll_msg_add_dev_handle(struct sk_buff *msg, struct 
>> dpll_device *dpll)
>>       return 0;
>>   }
>> +/**
>> + * dpll_msg_pin_handle_size - get size of pin handle attribute for 
>> given pin
>> + * @pin: pin pointer
>> + *
>> + * Return: byte size of pin handle attribute for given pin.
>> + */
>> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
>> +{
>> +    return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
>> +}
>> +EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
>> +
>>   /**
>>    * dpll_msg_add_pin_handle - attach pin handle attribute to a given 
>> message
>>    * @msg: pointer to sk_buff message to attach a pin handle
>> @@ -54,6 +66,7 @@ int dpll_msg_add_pin_handle(struct sk_buff *msg, 
>> struct dpll_pin *pin)
>>           return -EMSGSIZE;
>>       return 0;
>>   }
>> +EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
> 
> Jiri, could you please remind me what is the reason to export this 
> function? Because I cannot
> any usage of this function in drivers.
> 

Ah, found it, sorry for the noise.

> 
> [....]


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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-08-01 14:50           ` Kubalewski, Arkadiusz
@ 2023-08-02  6:57             ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-08-02  6:57 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, July 31, 2023 2:20 PM
>>
>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>
>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>
>>[...]
>>
>>
>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>*extack)
>>>>>+{
>>>>>+	int i;
>>>>>+
>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>
>>>>And again, as I already told you, this flag checking is totally
>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>
>>>
>>>This is not pointless, will explain below.
>>>
>>>>
>>>>
>>>
>>>[...]
>>>
>>
>>[...]
>>
>>
>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>+{
>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>+
>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>+		return;
>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>+
>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>+	ice_dpll_deinit_info(pf);
>>>>>+	if (cgu)
>>>>>+		ice_dpll_deinit_worker(pf);
>>>>
>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>currently.
>>>>
>>>
>>>Not true.
>>>The feature flag is common approach in ice. If the feature was successfully
>>
>>The fact that something is common does not necessarily mean it is
>>correct. 0 value argument.
>>
>
>Like using functions that unwrap netlink attributes as unsigned when
>they are in fact enums with possibility of being signed?

Looks this is bothering you, sorry about that.


>
>This is about consistent approach in ice driver.
>
>>
>>>initialized the flag is set. It allows to determine if deinit of the feature
>>>is required on driver unload.
>>>
>>>Right now the check for the flag is not only in kworker but also in each
>>>callback, if the flag were cleared the data shall be not accessed by
>>>callbacks.
>>
>>Could you please draw me a scenario when this could actually happen?
>>It is just a matter of ordering. Unregister dpll device/pins before you
>>cleanup the related resources and you don't need this ridiculous flag.
>>
>
>Flag allows to determine if dpll was successfully initialized and do proper
>deinit on rmmod only if it was initialized. That's all.

You are not answering my question. I asked about how the flag helps is
you do unregister dpll devices/pins and you free related resources in
the correct order. Because that is why you claim you need this flag.

I'm tired of this. Keep your driver tangled for all I care, I'm trying
to help you, obviously you are not interested.


>
>>
>>>I know this is not required, but it helps on loading and unloading the
>>>driver,
>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>load/unload.
>>
>>? Could you plese draw me a scenario how such thing may actually happen?
>
>First of all I said it is not required.
>
>I already draw you this with above sentence.
>You need spam pin-get asynchronously and unload driver, what is not clear?
>Basically mutex in dpll is a bottleneck, with multiple requests waiting for
>mutex there is low change of driver getting mutex when doing unregisters.

How exactly your flag helps you in this scenario? It does not.


>
>We actually need to redesign the mutex in dpll core/netlink, but I guess after
>initial submission.

Why?


>
>Thank you!
>Arkadiusz
>
>>
>>Thanks!
>>
>>
>>>
>>>>
>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>+}
>>
>>
>>[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-08-02  6:57             ` Jiri Pirko
  0 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-08-02  6:57 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, July 31, 2023 2:20 PM
>>
>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>
>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>
>>
>>[...]
>>
>>
>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>*extack)
>>>>>+{
>>>>>+	int i;
>>>>>+
>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>
>>>>And again, as I already told you, this flag checking is totally
>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>
>>>
>>>This is not pointless, will explain below.
>>>
>>>>
>>>>
>>>
>>>[...]
>>>
>>
>>[...]
>>
>>
>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>+{
>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>+
>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>+		return;
>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>+
>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>+	ice_dpll_deinit_info(pf);
>>>>>+	if (cgu)
>>>>>+		ice_dpll_deinit_worker(pf);
>>>>
>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>currently.
>>>>
>>>
>>>Not true.
>>>The feature flag is common approach in ice. If the feature was successfully
>>
>>The fact that something is common does not necessarily mean it is
>>correct. 0 value argument.
>>
>
>Like using functions that unwrap netlink attributes as unsigned when
>they are in fact enums with possibility of being signed?

Looks this is bothering you, sorry about that.


>
>This is about consistent approach in ice driver.
>
>>
>>>initialized the flag is set. It allows to determine if deinit of the feature
>>>is required on driver unload.
>>>
>>>Right now the check for the flag is not only in kworker but also in each
>>>callback, if the flag were cleared the data shall be not accessed by
>>>callbacks.
>>
>>Could you please draw me a scenario when this could actually happen?
>>It is just a matter of ordering. Unregister dpll device/pins before you
>>cleanup the related resources and you don't need this ridiculous flag.
>>
>
>Flag allows to determine if dpll was successfully initialized and do proper
>deinit on rmmod only if it was initialized. That's all.

You are not answering my question. I asked about how the flag helps is
you do unregister dpll devices/pins and you free related resources in
the correct order. Because that is why you claim you need this flag.

I'm tired of this. Keep your driver tangled for all I care, I'm trying
to help you, obviously you are not interested.


>
>>
>>>I know this is not required, but it helps on loading and unloading the
>>>driver,
>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>load/unload.
>>
>>? Could you plese draw me a scenario how such thing may actually happen?
>
>First of all I said it is not required.
>
>I already draw you this with above sentence.
>You need spam pin-get asynchronously and unload driver, what is not clear?
>Basically mutex in dpll is a bottleneck, with multiple requests waiting for
>mutex there is low change of driver getting mutex when doing unregisters.

How exactly your flag helps you in this scenario? It does not.


>
>We actually need to redesign the mutex in dpll core/netlink, but I guess after
>initial submission.

Why?


>
>Thank you!
>Arkadiusz
>
>>
>>Thanks!
>>
>>
>>>
>>>>
>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>+}
>>
>>
>>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-08-02  6:57             ` Jiri Pirko
@ 2023-08-02 15:48               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-02 15:48 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, August 2, 2023 8:57 AM
>
>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, July 31, 2023 2:20 PM
>>>
>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>
>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>
>>>
>>>[...]
>>>
>>>
>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>>*extack)
>>>>>>+{
>>>>>>+	int i;
>>>>>>+
>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>
>>>>>And again, as I already told you, this flag checking is totally
>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>
>>>>
>>>>This is not pointless, will explain below.
>>>>
>>>>>
>>>>>
>>>>
>>>>[...]
>>>>
>>>
>>>[...]
>>>
>>>
>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>+{
>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>+
>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>+		return;
>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>+
>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>+	if (cgu)
>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>
>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>currently.
>>>>>
>>>>
>>>>Not true.
>>>>The feature flag is common approach in ice. If the feature was
>>>>successfully
>>>
>>>The fact that something is common does not necessarily mean it is
>>>correct. 0 value argument.
>>>
>>
>>Like using functions that unwrap netlink attributes as unsigned when
>>they are in fact enums with possibility of being signed?
>
>Looks this is bothering you, sorry about that.
>

Just poining out.

>
>>
>>This is about consistent approach in ice driver.
>>
>>>
>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>feature
>>>>is required on driver unload.
>>>>
>>>>Right now the check for the flag is not only in kworker but also in each
>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>callbacks.
>>>
>>>Could you please draw me a scenario when this could actually happen?
>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>cleanup the related resources and you don't need this ridiculous flag.
>>>
>>
>>Flag allows to determine if dpll was successfully initialized and do
>>proper
>>deinit on rmmod only if it was initialized. That's all.
>
>You are not answering my question. I asked about how the flag helps is
>you do unregister dpll devices/pins and you free related resources in
>the correct order. Because that is why you claim you need this flag.
>

I do not claim such thing, actually opposite, I said it helps a bit
but the reason for existence is different, yet you are still trying to
imply me this.

>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>to help you, obviously you are not interested.
>

With review you are doing great job and many thanks for that.

Already said it multiple times, the main reason of flag existence is not a
use in the callback but to determine successful dpll initialization.
As there is no need to call unregister on anything if it was not successfully
registered.

>
>>
>>>
>>>>I know this is not required, but it helps on loading and unloading the
>>>>driver,
>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>load/unload.
>>>
>>>? Could you plese draw me a scenario how such thing may actually happen?
>>
>>First of all I said it is not required.
>>
>>I already draw you this with above sentence.
>>You need spam pin-get asynchronously and unload driver, what is not clear?
>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>for
>>mutex there is low change of driver getting mutex when doing unregisters.
>
>How exactly your flag helps you in this scenario? It does not.
>

In this scenario it helps because it fails the callbacks when dpll subsystem
was partially initialized and callbacks can be already invoked, but in fact
the dpll initialization is not yet finished in the driver, and there will always
be the time between first and second dpll registration where we might wait for
the mutex to become available on dpll core part.

>
>>
>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>after
>>initial submission.
>
>Why?
>

The global mutex for accessing the data works just fine, but it is slow.
Maybe we could improve this by using rwlock instead.

Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>Thanks!
>>>
>>>
>>>>
>>>>>
>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>+}
>>>
>>>
>>>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-08-02 15:48               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-02 15:48 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, August 2, 2023 8:57 AM
>
>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, July 31, 2023 2:20 PM
>>>
>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>
>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>
>>>
>>>[...]
>>>
>>>
>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>>*extack)
>>>>>>+{
>>>>>>+	int i;
>>>>>>+
>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>
>>>>>And again, as I already told you, this flag checking is totally
>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>
>>>>
>>>>This is not pointless, will explain below.
>>>>
>>>>>
>>>>>
>>>>
>>>>[...]
>>>>
>>>
>>>[...]
>>>
>>>
>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>+{
>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>+
>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>+		return;
>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>+
>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>+	if (cgu)
>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>
>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>currently.
>>>>>
>>>>
>>>>Not true.
>>>>The feature flag is common approach in ice. If the feature was
>>>>successfully
>>>
>>>The fact that something is common does not necessarily mean it is
>>>correct. 0 value argument.
>>>
>>
>>Like using functions that unwrap netlink attributes as unsigned when
>>they are in fact enums with possibility of being signed?
>
>Looks this is bothering you, sorry about that.
>

Just poining out.

>
>>
>>This is about consistent approach in ice driver.
>>
>>>
>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>feature
>>>>is required on driver unload.
>>>>
>>>>Right now the check for the flag is not only in kworker but also in each
>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>callbacks.
>>>
>>>Could you please draw me a scenario when this could actually happen?
>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>cleanup the related resources and you don't need this ridiculous flag.
>>>
>>
>>Flag allows to determine if dpll was successfully initialized and do
>>proper
>>deinit on rmmod only if it was initialized. That's all.
>
>You are not answering my question. I asked about how the flag helps is
>you do unregister dpll devices/pins and you free related resources in
>the correct order. Because that is why you claim you need this flag.
>

I do not claim such thing, actually opposite, I said it helps a bit
but the reason for existence is different, yet you are still trying to
imply me this.

>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>to help you, obviously you are not interested.
>

With review you are doing great job and many thanks for that.

Already said it multiple times, the main reason of flag existence is not a
use in the callback but to determine successful dpll initialization.
As there is no need to call unregister on anything if it was not successfully
registered.

>
>>
>>>
>>>>I know this is not required, but it helps on loading and unloading the
>>>>driver,
>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>load/unload.
>>>
>>>? Could you plese draw me a scenario how such thing may actually happen?
>>
>>First of all I said it is not required.
>>
>>I already draw you this with above sentence.
>>You need spam pin-get asynchronously and unload driver, what is not clear?
>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>for
>>mutex there is low change of driver getting mutex when doing unregisters.
>
>How exactly your flag helps you in this scenario? It does not.
>

In this scenario it helps because it fails the callbacks when dpll subsystem
was partially initialized and callbacks can be already invoked, but in fact
the dpll initialization is not yet finished in the driver, and there will always
be the time between first and second dpll registration where we might wait for
the mutex to become available on dpll core part.

>
>>
>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>after
>>initial submission.
>
>Why?
>

The global mutex for accessing the data works just fine, but it is slow.
Maybe we could improve this by using rwlock instead.

Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>Thanks!
>>>
>>>
>>>>
>>>>>
>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>+}
>>>
>>>
>>>[...]

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

* Re: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-08-02 15:48               ` Kubalewski, Arkadiusz
@ 2023-08-03  8:02                 ` Jiri Pirko
  -1 siblings, 0 replies; 109+ messages in thread
From: Jiri Pirko @ 2023-08-03  8:02 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

Wed, Aug 02, 2023 at 05:48:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, August 2, 2023 8:57 AM
>>
>>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, July 31, 2023 2:20 PM
>>>>
>>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>>>*extack)
>>>>>>>+{
>>>>>>>+	int i;
>>>>>>>+
>>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>>
>>>>>>And again, as I already told you, this flag checking is totally
>>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>>
>>>>>
>>>>>This is not pointless, will explain below.
>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>>+{
>>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>>+
>>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>>+		return;
>>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>>+
>>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>>+	if (cgu)
>>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>>
>>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>>currently.
>>>>>>
>>>>>
>>>>>Not true.
>>>>>The feature flag is common approach in ice. If the feature was
>>>>>successfully
>>>>
>>>>The fact that something is common does not necessarily mean it is
>>>>correct. 0 value argument.
>>>>
>>>
>>>Like using functions that unwrap netlink attributes as unsigned when
>>>they are in fact enums with possibility of being signed?
>>
>>Looks this is bothering you, sorry about that.
>>
>
>Just poining out.
>
>>
>>>
>>>This is about consistent approach in ice driver.
>>>
>>>>
>>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>>feature
>>>>>is required on driver unload.
>>>>>
>>>>>Right now the check for the flag is not only in kworker but also in each
>>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>>callbacks.
>>>>
>>>>Could you please draw me a scenario when this could actually happen?
>>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>>cleanup the related resources and you don't need this ridiculous flag.
>>>>
>>>
>>>Flag allows to determine if dpll was successfully initialized and do
>>>proper
>>>deinit on rmmod only if it was initialized. That's all.
>>
>>You are not answering my question. I asked about how the flag helps is
>>you do unregister dpll devices/pins and you free related resources in
>>the correct order. Because that is why you claim you need this flag.
>>
>
>I do not claim such thing, actually opposite, I said it helps a bit
>but the reason for existence is different, yet you are still trying to
>imply me this.
>
>>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>>to help you, obviously you are not interested.
>>
>
>With review you are doing great job and many thanks for that.
>
>Already said it multiple times, the main reason of flag existence is not a
>use in the callback but to determine successful dpll initialization.

So use it only for this, nothing else. Use it only to check during
cleanup that you need to do the cleanup as init was previously done.


>As there is no need to call unregister on anything if it was not successfully
>registered.
>
>>
>>>
>>>>
>>>>>I know this is not required, but it helps on loading and unloading the
>>>>>driver,
>>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>>load/unload.
>>>>
>>>>? Could you plese draw me a scenario how such thing may actually happen?
>>>
>>>First of all I said it is not required.
>>>
>>>I already draw you this with above sentence.
>>>You need spam pin-get asynchronously and unload driver, what is not clear?
>>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>>for
>>>mutex there is low change of driver getting mutex when doing unregisters.
>>
>>How exactly your flag helps you in this scenario? It does not.
>>
>
>In this scenario it helps because it fails the callbacks when dpll subsystem
>was partially initialized and callbacks can be already invoked, but in fact
>the dpll initialization is not yet finished in the driver, and there will always
>be the time between first and second dpll registration where we might wait for
>the mutex to become available on dpll core part.

Draw it to me, please, where exatly there is a problem. I'm still
convinced that with the proper ordering of init/cleanup flows,
you'll get all you need, without any flag use.


>
>>
>>>
>>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>>after
>>>initial submission.
>>
>>Why?
>>
>
>The global mutex for accessing the data works just fine, but it is slow.
>Maybe we could improve this by using rwlock instead.

"it is slow" is quite vague description of what's wrong with the
locking.


>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>Thanks!
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>>+}
>>>>
>>>>
>>>>[...]

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

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

Wed, Aug 02, 2023 at 05:48:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, August 2, 2023 8:57 AM
>>
>>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, July 31, 2023 2:20 PM
>>>>
>>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>>
>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev wrote:
>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct netlink_ext_ack
>>>>>>>*extack)
>>>>>>>+{
>>>>>>>+	int i;
>>>>>>>+
>>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>>
>>>>>>And again, as I already told you, this flag checking is totally
>>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>>
>>>>>
>>>>>This is not pointless, will explain below.
>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>>+{
>>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>>+
>>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>>+		return;
>>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>>+
>>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>>+	if (cgu)
>>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>>
>>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as the
>>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>>currently.
>>>>>>
>>>>>
>>>>>Not true.
>>>>>The feature flag is common approach in ice. If the feature was
>>>>>successfully
>>>>
>>>>The fact that something is common does not necessarily mean it is
>>>>correct. 0 value argument.
>>>>
>>>
>>>Like using functions that unwrap netlink attributes as unsigned when
>>>they are in fact enums with possibility of being signed?
>>
>>Looks this is bothering you, sorry about that.
>>
>
>Just poining out.
>
>>
>>>
>>>This is about consistent approach in ice driver.
>>>
>>>>
>>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>>feature
>>>>>is required on driver unload.
>>>>>
>>>>>Right now the check for the flag is not only in kworker but also in each
>>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>>callbacks.
>>>>
>>>>Could you please draw me a scenario when this could actually happen?
>>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>>cleanup the related resources and you don't need this ridiculous flag.
>>>>
>>>
>>>Flag allows to determine if dpll was successfully initialized and do
>>>proper
>>>deinit on rmmod only if it was initialized. That's all.
>>
>>You are not answering my question. I asked about how the flag helps is
>>you do unregister dpll devices/pins and you free related resources in
>>the correct order. Because that is why you claim you need this flag.
>>
>
>I do not claim such thing, actually opposite, I said it helps a bit
>but the reason for existence is different, yet you are still trying to
>imply me this.
>
>>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>>to help you, obviously you are not interested.
>>
>
>With review you are doing great job and many thanks for that.
>
>Already said it multiple times, the main reason of flag existence is not a
>use in the callback but to determine successful dpll initialization.

So use it only for this, nothing else. Use it only to check during
cleanup that you need to do the cleanup as init was previously done.


>As there is no need to call unregister on anything if it was not successfully
>registered.
>
>>
>>>
>>>>
>>>>>I know this is not required, but it helps on loading and unloading the
>>>>>driver,
>>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>>load/unload.
>>>>
>>>>? Could you plese draw me a scenario how such thing may actually happen?
>>>
>>>First of all I said it is not required.
>>>
>>>I already draw you this with above sentence.
>>>You need spam pin-get asynchronously and unload driver, what is not clear?
>>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>>for
>>>mutex there is low change of driver getting mutex when doing unregisters.
>>
>>How exactly your flag helps you in this scenario? It does not.
>>
>
>In this scenario it helps because it fails the callbacks when dpll subsystem
>was partially initialized and callbacks can be already invoked, but in fact
>the dpll initialization is not yet finished in the driver, and there will always
>be the time between first and second dpll registration where we might wait for
>the mutex to become available on dpll core part.

Draw it to me, please, where exatly there is a problem. I'm still
convinced that with the proper ordering of init/cleanup flows,
you'll get all you need, without any flag use.


>
>>
>>>
>>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>>after
>>>initial submission.
>>
>>Why?
>>
>
>The global mutex for accessing the data works just fine, but it is slow.
>Maybe we could improve this by using rwlock instead.

"it is slow" is quite vague description of what's wrong with the
locking.


>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>Thank you!
>>>Arkadiusz
>>>
>>>>
>>>>Thanks!
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>>+}
>>>>
>>>>
>>>>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
  2023-08-03  8:02                 ` Jiri Pirko
@ 2023-08-04  8:58                   ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-04  8:58 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, August 3, 2023 10:03 AM
>
>Wed, Aug 02, 2023 at 05:48:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, August 2, 2023 8:57 AM
>>>
>>>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Monday, July 31, 2023 2:20 PM
>>>>>
>>>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>>>
>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>wrote:
>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct
>>>>>>>>netlink_ext_ack
>>>>>>>>*extack)
>>>>>>>>+{
>>>>>>>>+	int i;
>>>>>>>>+
>>>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>>>
>>>>>>>And again, as I already told you, this flag checking is totally
>>>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>>>
>>>>>>
>>>>>>This is not pointless, will explain below.
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>[...]
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>>>+{
>>>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>>>+
>>>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>>>+		return;
>>>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>>>+
>>>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>>>+	if (cgu)
>>>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>>>
>>>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as
>>>>>>>the
>>>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>>>currently.
>>>>>>>
>>>>>>
>>>>>>Not true.
>>>>>>The feature flag is common approach in ice. If the feature was
>>>>>>successfully
>>>>>
>>>>>The fact that something is common does not necessarily mean it is
>>>>>correct. 0 value argument.
>>>>>
>>>>
>>>>Like using functions that unwrap netlink attributes as unsigned when
>>>>they are in fact enums with possibility of being signed?
>>>
>>>Looks this is bothering you, sorry about that.
>>>
>>
>>Just poining out.
>>
>>>
>>>>
>>>>This is about consistent approach in ice driver.
>>>>
>>>>>
>>>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>>>feature
>>>>>>is required on driver unload.
>>>>>>
>>>>>>Right now the check for the flag is not only in kworker but also in
>>>>>>each
>>>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>>>callbacks.
>>>>>
>>>>>Could you please draw me a scenario when this could actually happen?
>>>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>>>cleanup the related resources and you don't need this ridiculous flag.
>>>>>
>>>>
>>>>Flag allows to determine if dpll was successfully initialized and do
>>>>proper
>>>>deinit on rmmod only if it was initialized. That's all.
>>>
>>>You are not answering my question. I asked about how the flag helps is
>>>you do unregister dpll devices/pins and you free related resources in
>>>the correct order. Because that is why you claim you need this flag.
>>>
>>
>>I do not claim such thing, actually opposite, I said it helps a bit
>>but the reason for existence is different, yet you are still trying to
>>imply me this.
>>
>>>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>>>to help you, obviously you are not interested.
>>>
>>
>>With review you are doing great job and many thanks for that.
>>
>>Already said it multiple times, the main reason of flag existence is not a
>>use in the callback but to determine successful dpll initialization.
>
>So use it only for this, nothing else. Use it only to check during
>cleanup that you need to do the cleanup as init was previously done.
>

Ok, will do.

>
>>As there is no need to call unregister on anything if it was not
>successfully
>>registered.
>>
>>>
>>>>
>>>>>
>>>>>>I know this is not required, but it helps on loading and unloading the
>>>>>>driver,
>>>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>>>load/unload.
>>>>>
>>>>>? Could you plese draw me a scenario how such thing may actually
>>>>>happen?
>>>>
>>>>First of all I said it is not required.
>>>>
>>>>I already draw you this with above sentence.
>>>>You need spam pin-get asynchronously and unload driver, what is not
>>>>clear?
>>>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>>>for
>>>>mutex there is low change of driver getting mutex when doing
>>>>unregisters.
>>>
>>>How exactly your flag helps you in this scenario? It does not.
>>>
>>
>>In this scenario it helps because it fails the callbacks when dpll
>>subsystem
>>was partially initialized and callbacks can be already invoked, but in
>>fact
>>the dpll initialization is not yet finished in the driver, and there will
>>always
>>be the time between first and second dpll registration where we might wait
>>for
>>the mutex to become available on dpll core part.
>
>Draw it to me, please, where exatly there is a problem. I'm still
>convinced that with the proper ordering of init/cleanup flows,
>you'll get all you need, without any flag use.
>

But I never said there is some issue, was saying from the beginning
"helping a bit" and "not required". Sorry I don't know how to draw this
other than above.

As agreed, will fix and use it only to deinit, let's move on, it is not
required :)

>
>>
>>>
>>>>
>>>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>>>after
>>>>initial submission.
>>>
>>>Why?
>>>
>>
>>The global mutex for accessing the data works just fine, but it is slow.
>>Maybe we could improve this by using rwlock instead.
>
>"it is slow" is quite vague description of what's wrong with the
>locking.
>

I mean serialized access to dpll is something that might be the issue in the
OS with multiple pins/devices and tools monitoring them, no hard data so far.


Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>Thanks!
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>>>+}
>>>>>
>>>>>
>>>>>[...]

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

* RE: [PATCH 09/11] ice: implement dpll interface to control cgu
@ 2023-08-04  8:58                   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 109+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-08-04  8:58 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	Olech, Milena, Michalik, Michal, linux-arm-kernel, poros,
	mschmidt, netdev, linux-clk, Bart Van Assche

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, August 3, 2023 10:03 AM
>
>Wed, Aug 02, 2023 at 05:48:43PM CEST, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, August 2, 2023 8:57 AM
>>>
>>>Tue, Aug 01, 2023 at 04:50:44PM CEST, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Monday, July 31, 2023 2:20 PM
>>>>>
>>>>>Sat, Jul 29, 2023 at 01:03:59AM CEST, arkadiusz.kubalewski@intel.com
>>>>>wrote:
>>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>>Sent: Friday, July 21, 2023 1:39 PM
>>>>>>>
>>>>>>>Thu, Jul 20, 2023 at 11:19:01AM CEST, vadim.fedorenko@linux.dev
>>>>>>>wrote:
>>>>>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>>>+static int ice_dpll_cb_lock(struct ice_pf *pf, struct
>>>>>>>>netlink_ext_ack
>>>>>>>>*extack)
>>>>>>>>+{
>>>>>>>>+	int i;
>>>>>>>>+
>>>>>>>>+	for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>>>>>>+		if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>>>>>>
>>>>>>>And again, as I already told you, this flag checking is totally
>>>>>>>pointless. See below my comment to ice_dpll_init()/ice_dpll_deinit().
>>>>>>>
>>>>>>
>>>>>>This is not pointless, will explain below.
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>[...]
>>>>>>
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>>>+void ice_dpll_deinit(struct ice_pf *pf)
>>>>>>>>+{
>>>>>>>>+	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>>>>>>>+
>>>>>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>>>>>>+		return;
>>>>>>>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>>>>>>+
>>>>>>>>+	ice_dpll_deinit_pins(pf, cgu);
>>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>>>>>>+	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>>>>>>>+	ice_dpll_deinit_info(pf);
>>>>>>>>+	if (cgu)
>>>>>>>>+		ice_dpll_deinit_worker(pf);
>>>>>>>
>>>>>>>Could you please order the ice_dpll_deinit() to be symmetrical to
>>>>>>>ice_dpll_init()? Then, you can drop ICE_FLAG_DPLL flag entirely, as
>>>>>>>the
>>>>>>>ice_dpll_periodic_work() function is the only reason why you need it
>>>>>>>currently.
>>>>>>>
>>>>>>
>>>>>>Not true.
>>>>>>The feature flag is common approach in ice. If the feature was
>>>>>>successfully
>>>>>
>>>>>The fact that something is common does not necessarily mean it is
>>>>>correct. 0 value argument.
>>>>>
>>>>
>>>>Like using functions that unwrap netlink attributes as unsigned when
>>>>they are in fact enums with possibility of being signed?
>>>
>>>Looks this is bothering you, sorry about that.
>>>
>>
>>Just poining out.
>>
>>>
>>>>
>>>>This is about consistent approach in ice driver.
>>>>
>>>>>
>>>>>>initialized the flag is set. It allows to determine if deinit of the
>>>>>>feature
>>>>>>is required on driver unload.
>>>>>>
>>>>>>Right now the check for the flag is not only in kworker but also in
>>>>>>each
>>>>>>callback, if the flag were cleared the data shall be not accessed by
>>>>>>callbacks.
>>>>>
>>>>>Could you please draw me a scenario when this could actually happen?
>>>>>It is just a matter of ordering. Unregister dpll device/pins before you
>>>>>cleanup the related resources and you don't need this ridiculous flag.
>>>>>
>>>>
>>>>Flag allows to determine if dpll was successfully initialized and do
>>>>proper
>>>>deinit on rmmod only if it was initialized. That's all.
>>>
>>>You are not answering my question. I asked about how the flag helps is
>>>you do unregister dpll devices/pins and you free related resources in
>>>the correct order. Because that is why you claim you need this flag.
>>>
>>
>>I do not claim such thing, actually opposite, I said it helps a bit
>>but the reason for existence is different, yet you are still trying to
>>imply me this.
>>
>>>I'm tired of this. Keep your driver tangled for all I care, I'm trying
>>>to help you, obviously you are not interested.
>>>
>>
>>With review you are doing great job and many thanks for that.
>>
>>Already said it multiple times, the main reason of flag existence is not a
>>use in the callback but to determine successful dpll initialization.
>
>So use it only for this, nothing else. Use it only to check during
>cleanup that you need to do the cleanup as init was previously done.
>

Ok, will do.

>
>>As there is no need to call unregister on anything if it was not
>successfully
>>registered.
>>
>>>
>>>>
>>>>>
>>>>>>I know this is not required, but it helps on loading and unloading the
>>>>>>driver,
>>>>>>thanks to that, spam of pin-get dump is not slowing the driver
>>>>>>load/unload.
>>>>>
>>>>>? Could you plese draw me a scenario how such thing may actually
>>>>>happen?
>>>>
>>>>First of all I said it is not required.
>>>>
>>>>I already draw you this with above sentence.
>>>>You need spam pin-get asynchronously and unload driver, what is not
>>>>clear?
>>>>Basically mutex in dpll is a bottleneck, with multiple requests waiting
>>>>for
>>>>mutex there is low change of driver getting mutex when doing
>>>>unregisters.
>>>
>>>How exactly your flag helps you in this scenario? It does not.
>>>
>>
>>In this scenario it helps because it fails the callbacks when dpll
>>subsystem
>>was partially initialized and callbacks can be already invoked, but in
>>fact
>>the dpll initialization is not yet finished in the driver, and there will
>>always
>>be the time between first and second dpll registration where we might wait
>>for
>>the mutex to become available on dpll core part.
>
>Draw it to me, please, where exatly there is a problem. I'm still
>convinced that with the proper ordering of init/cleanup flows,
>you'll get all you need, without any flag use.
>

But I never said there is some issue, was saying from the beginning
"helping a bit" and "not required". Sorry I don't know how to draw this
other than above.

As agreed, will fix and use it only to deinit, let's move on, it is not
required :)

>
>>
>>>
>>>>
>>>>We actually need to redesign the mutex in dpll core/netlink, but I guess
>>>>after
>>>>initial submission.
>>>
>>>Why?
>>>
>>
>>The global mutex for accessing the data works just fine, but it is slow.
>>Maybe we could improve this by using rwlock instead.
>
>"it is slow" is quite vague description of what's wrong with the
>locking.
>

I mean serialized access to dpll is something that might be the issue in the
OS with multiple pins/devices and tools monitoring them, no hard data so far.


Thank you!
Arkadiusz

>
>>
>>Thank you!
>>Arkadiusz
>>
>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz
>>>>
>>>>>
>>>>>Thanks!
>>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>>>+	mutex_destroy(&pf->dplls.lock);
>>>>>>>>+}
>>>>>
>>>>>
>>>>>[...]

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

end of thread, other threads:[~2023-08-04  8:58 UTC | newest]

Thread overview: 109+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-20  9:18 [PATCH net-next 00/11] Create common DPLL configuration API Vadim Fedorenko
2023-07-20  9:18 ` Vadim Fedorenko
2023-07-20  9:18 ` [PATCH net-next 01/11] tools: ynl-gen: fix enum index in _decode_enum(..) Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-20 13:40   ` Jiri Pirko
2023-07-20 13:40     ` Jiri Pirko
2023-07-20 13:58     ` Kubalewski, Arkadiusz
2023-07-20 13:58       ` Kubalewski, Arkadiusz
2023-07-20 14:48       ` Vadim Fedorenko
2023-07-20 14:48         ` Vadim Fedorenko
2023-07-20  9:18 ` [PATCH net-next 02/11] tools: ynl-gen: fix parse multi-attr enum attribute Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-20  9:18 ` [PATCH net-next 03/11] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-20 13:47   ` Jiri Pirko
2023-07-20 13:47     ` Jiri Pirko
2023-07-20  9:18 ` [PATCH net-next 04/11] dpll: spec: Add Netlink spec in YAML Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-24 15:52   ` Simon Horman
2023-07-20  9:18 ` [PATCH net-next 05/11] dpll: core: Add DPLL framework base functions Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-24 15:56   ` Simon Horman
2023-07-20  9:18 ` [PATCH net-next 06/11] dpll: netlink: " Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-07-24 16:34   ` Simon Horman
2023-08-01 12:23   ` Jiri Pirko
2023-08-01 12:23     ` Jiri Pirko
2023-07-20  9:18 ` [PATCH net-next 07/11] netdev: expose DPLL pin handle for netdevice Vadim Fedorenko
2023-07-20  9:18   ` Vadim Fedorenko
2023-08-01 13:23   ` Vadim Fedorenko
2023-08-01 13:23     ` Vadim Fedorenko
2023-08-01 15:12     ` Vadim Fedorenko
2023-08-01 15:12       ` Vadim Fedorenko
2023-07-20  9:19 ` [PATCH net-next 08/11] ice: add admin commands to access cgu configuration Vadim Fedorenko
2023-07-20  9:19   ` Vadim Fedorenko
2023-07-24 17:21   ` Simon Horman
2023-07-28 12:46     ` Kubalewski, Arkadiusz
2023-07-28 12:46       ` Kubalewski, Arkadiusz
2023-07-20  9:19 ` [PATCH 09/11] ice: implement dpll interface to control cgu Vadim Fedorenko
2023-07-20  9:19   ` Vadim Fedorenko
2023-07-20 14:08   ` Jiri Pirko
2023-07-20 14:08     ` Jiri Pirko
2023-07-20 17:31     ` Kubalewski, Arkadiusz
2023-07-20 17:31       ` Kubalewski, Arkadiusz
2023-07-21  7:33       ` Jiri Pirko
2023-07-21  7:33         ` Jiri Pirko
2023-07-21 11:17         ` Kubalewski, Arkadiusz
2023-07-21 11:17           ` Kubalewski, Arkadiusz
2023-07-21 12:02           ` Jiri Pirko
2023-07-21 12:02             ` Jiri Pirko
2023-07-21 13:36             ` Kubalewski, Arkadiusz
2023-07-21 13:36               ` Kubalewski, Arkadiusz
2023-07-21 15:45               ` Jiri Pirko
2023-07-21 15:45                 ` Jiri Pirko
2023-07-21 19:48                 ` Kubalewski, Arkadiusz
2023-07-21 19:48                   ` Kubalewski, Arkadiusz
2023-07-22  6:37                   ` Jiri Pirko
2023-07-22  6:37                     ` Jiri Pirko
2023-07-24 15:03                     ` Kubalewski, Arkadiusz
2023-07-25  8:03                       ` Jiri Pirko
2023-07-25 14:01                         ` Kubalewski, Arkadiusz
2023-07-26  6:38                           ` Jiri Pirko
2023-07-26 21:11                             ` Kubalewski, Arkadiusz
2023-07-27 10:28                               ` Vadim Fedorenko
2023-07-27 10:28                                 ` Vadim Fedorenko
2023-07-25 22:49             ` Jakub Kicinski
2023-07-26 15:20               ` Paolo Abeni
2023-07-26 21:10                 ` Kubalewski, Arkadiusz
2023-07-31 12:08                 ` Jiri Pirko
2023-07-31 12:08                   ` Jiri Pirko
2023-07-26 21:08               ` Kubalewski, Arkadiusz
2023-07-31 12:11               ` Jiri Pirko
2023-07-31 12:11                 ` Jiri Pirko
2023-07-22  2:08         ` Jakub Kicinski
2023-07-22  2:08           ` Jakub Kicinski
2023-07-21 11:39   ` Jiri Pirko
2023-07-21 11:39     ` Jiri Pirko
2023-07-28 23:03     ` Kubalewski, Arkadiusz
2023-07-28 23:03       ` Kubalewski, Arkadiusz
2023-07-31 12:19       ` Jiri Pirko
2023-07-31 12:19         ` Jiri Pirko
2023-08-01 14:50         ` Kubalewski, Arkadiusz
2023-08-01 14:50           ` Kubalewski, Arkadiusz
2023-08-02  6:57           ` Jiri Pirko
2023-08-02  6:57             ` Jiri Pirko
2023-08-02 15:48             ` Kubalewski, Arkadiusz
2023-08-02 15:48               ` Kubalewski, Arkadiusz
2023-08-03  8:02               ` Jiri Pirko
2023-08-03  8:02                 ` Jiri Pirko
2023-08-04  8:58                 ` Kubalewski, Arkadiusz
2023-08-04  8:58                   ` Kubalewski, Arkadiusz
2023-07-24 17:41   ` Simon Horman
2023-07-24 17:58     ` Vadim Fedorenko
2023-07-28 23:10       ` Kubalewski, Arkadiusz
2023-07-28 23:10         ` Kubalewski, Arkadiusz
2023-07-20  9:19 ` [PATCH 10/11] ptp_ocp: implement DPLL ops Vadim Fedorenko
2023-07-20  9:19   ` Vadim Fedorenko
2023-07-21 15:51   ` Jiri Pirko
2023-07-21 15:51     ` Jiri Pirko
2023-07-20  9:19 ` [PATCH 11/11] mlx5: Implement SyncE support using DPLL infrastructure Vadim Fedorenko
2023-07-20  9:19   ` Vadim Fedorenko
2023-07-21  7:48 ` [PATCH net-next 00/11] Create common DPLL configuration API Jiri Pirko
2023-07-21  7:48   ` Jiri Pirko
2023-07-21 11:14 ` Jiri Pirko
2023-07-21 11:14   ` Jiri Pirko
2023-07-21 14:42   ` Vadim Fedorenko
2023-07-21 14:42     ` Vadim Fedorenko
2023-07-21 15:46     ` Jiri Pirko
2023-07-21 15:46       ` 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.