mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 10:33:41 +02:00
Merge branch 'dpll-api'
Vadim Fedorenko says:
====================
Create common DPLL configuration API
Implement common API for DPLL configuration and status reporting.
The API utilises netlink interface as transport for commands and event
notifications. This API aims to extend current pin configuration
provided by PTP subsystem and make it flexible and easy to cover
complex 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:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--dump device-get
[{'clock-id': 4658613174691613800,
'id': 0,
'lock-status': 'locked-ho-acq',
'mode': 'automatic',
'mode-supported': ['automatic'],
'module-name': 'ice',
'type': 'eec'},
{'clock-id': 4658613174691613800,
'id': 1,
'lock-status': 'locked-ho-acq',
'mode': 'automatic',
'mode-supported': ['automatic'],
'module-name': 'ice',
'type': 'pps'}]
- get single pin info:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-get --json '{"id":2}'
{'board-label': 'C827_0-RCLKA',
'clock-id': 4658613174691613800,
'capabilities': 6,
'frequency': 1953125,
'id': 2,
'module-name': 'ice',
'parent-device': [{'direction': 'input',
'parent-id': 0,
'prio': 9,
'state': 'disconnected'},
{'direction': 'input',
'parent-id': 1,
'prio': 9,
'state': 'disconnected'}],
'type': 'mux'}
- set pin's state on dpll:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"id":2, "parent-device":{"parent-id":1, "state":2}}'
- set pin's prio on dpll:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"id":2, "parent-device":{"parent-id":1, "prio":4}}'
- set pin's state on parent pin:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"id":13, "parent-pin":{"parent-id":2, "state":1}}'
Changelog:
v7 -> v8:
- rebase on top of net-next
- no functional changes in patchset
v6 -> v7:
- use unique id in references array to prevent possible crashes
v5 -> v6:
- change dpll-caps to pin capabilities and adjust enum accordingly
- remove dpll.h from netdevice.h
v4 -> v5:
- separate namespace for pin attributes
- small fixes, more details in the patches
v3 -> v4:
- rebase on top of net-next
- fix flag usage in ice
v2 -> v3:
- more style and warning fixes
- details in per-patch logs
v1 -> v2:
- remove FREERUN/DETACHED mode
- reorder functions in commits not to depend on files introduced in
future commits
- style and warning fixes
v9 RFC -> v1:
- Merge header patch into the patches where the actual functions are
implemented
- Address comments from previous reviews
- Per patch change log contains more details
RFC versions:
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
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
037dbd122d
497
Documentation/driver-api/dpll.rst
Normal file
497
Documentation/driver-api/dpll.rst
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===============================
|
||||
The Linux kernel dpll subsystem
|
||||
===============================
|
||||
|
||||
DPLL
|
||||
====
|
||||
|
||||
PLL - Phase Locked Loop is an electronic circuit which syntonizes clock
|
||||
signal of a device with an external clock signal. Effectively enabling
|
||||
device to run on the same clock signal beat as provided on a PLL input.
|
||||
|
||||
DPLL - Digital Phase Locked Loop is an integrated circuit which in
|
||||
addition to plain PLL behavior incorporates a digital phase detector
|
||||
and may have digital divider in the loop. As a result, the frequency on
|
||||
DPLL's input and output may be configurable.
|
||||
|
||||
Subsystem
|
||||
=========
|
||||
|
||||
The main purpose of dpll subsystem is to provide general interface
|
||||
to configure devices that use any kind of Digital PLL and could use
|
||||
different sources of input signal to synchronize to, as well as
|
||||
different types of outputs.
|
||||
The main interface is NETLINK_GENERIC based protocol with an event
|
||||
monitoring multicast group defined.
|
||||
|
||||
Device object
|
||||
=============
|
||||
|
||||
Single dpll device object means single Digital PLL circuit and bunch of
|
||||
connected pins.
|
||||
It reports the supported modes of operation and current status to the
|
||||
user in response to the `do` request of netlink command
|
||||
``DPLL_CMD_DEVICE_GET`` and list of dplls registered in the subsystem
|
||||
with `dump` netlink request of the same command.
|
||||
Changing the configuration of dpll device is done with `do` request of
|
||||
netlink ``DPLL_CMD_DEVICE_SET`` command.
|
||||
A device handle is ``DPLL_A_ID``, it shall be provided to get or set
|
||||
configuration of particular device in the system. It can be obtained
|
||||
with a ``DPLL_CMD_DEVICE_GET`` `dump` request or
|
||||
a ``DPLL_CMD_DEVICE_ID_GET`` `do` request, where the one must provide
|
||||
attributes that result in single device match.
|
||||
|
||||
Pin object
|
||||
==========
|
||||
|
||||
A pin is amorphic object which represents either input or output, it
|
||||
could be internal component of the device, as well as externally
|
||||
connected.
|
||||
The number of pins per dpll vary, but usually multiple pins shall be
|
||||
provided for a single dpll device.
|
||||
Pin's properties, capabilities and status is provided to the user in
|
||||
response to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
|
||||
It is also possible to list all the pins that were registered in the
|
||||
system with `dump` request of ``DPLL_CMD_PIN_GET`` command.
|
||||
Configuration of a pin can be changed by `do` request of netlink
|
||||
``DPLL_CMD_PIN_SET`` command.
|
||||
Pin handle is a ``DPLL_A_PIN_ID``, it shall be provided to get or set
|
||||
configuration of particular pin in the system. It can be obtained with
|
||||
``DPLL_CMD_PIN_GET`` `dump` request or ``DPLL_CMD_PIN_ID_GET`` `do`
|
||||
request, where user provides attributes that result in single pin match.
|
||||
|
||||
Pin selection
|
||||
=============
|
||||
|
||||
In general, selected pin (the one which signal is driving the dpll
|
||||
device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
|
||||
one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
|
||||
device.
|
||||
|
||||
Pin selection can be done either manually or automatically, depending
|
||||
on hardware capabilities and active dpll device work mode
|
||||
(``DPLL_A_MODE`` attribute). The consequence is that there are
|
||||
differences for each mode in terms of available pin states, as well as
|
||||
for the states the user can request for a dpll device.
|
||||
|
||||
In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
|
||||
one of following pin states:
|
||||
|
||||
- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
|
||||
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
|
||||
device
|
||||
|
||||
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
|
||||
receive one of following pin states:
|
||||
|
||||
- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
|
||||
input for automatic selection algorithm
|
||||
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
|
||||
a valid input for automatic selection algorithm
|
||||
|
||||
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
|
||||
pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
|
||||
algorithm locks a dpll device with one of the inputs.
|
||||
|
||||
Shared pins
|
||||
===========
|
||||
|
||||
A single pin object can be attached to multiple dpll devices.
|
||||
Then there are two groups of configuration knobs:
|
||||
|
||||
1) Set on a pin - the configuration affects all dpll devices pin is
|
||||
registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
|
||||
2) Set on a pin-dpll tuple - the configuration affects only selected
|
||||
dpll device (i.e., ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
|
||||
``DPLL_A_PIN_DIRECTION``).
|
||||
|
||||
MUX-type pins
|
||||
=============
|
||||
|
||||
A pin can be MUX-type, it aggregates child pins and serves as a pin
|
||||
multiplexer. One or more pins are registered with MUX-type instead of
|
||||
being directly registered to a dpll device.
|
||||
Pins registered with a MUX-type pin provide user with additional nested
|
||||
attribute ``DPLL_A_PIN_PARENT_PIN`` for each parent they were registered
|
||||
with.
|
||||
If a pin was registered with multiple parent pins, they behave like a
|
||||
multiple output multiplexer. In this case output of a
|
||||
``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
|
||||
attributes with current state related to each parent, like:
|
||||
|
||||
'pin': [{{
|
||||
'clock-id': 282574471561216,
|
||||
'module-name': 'ice',
|
||||
'capabilities': 4,
|
||||
'id': 13,
|
||||
'parent-pin': [
|
||||
{'parent-id': 2, 'state': 'connected'},
|
||||
{'parent-id': 3, 'state': 'disconnected'}
|
||||
],
|
||||
'type': 'synce-eth-port'
|
||||
}}]
|
||||
|
||||
Only one child pin can provide its signal to the parent MUX-type pin at
|
||||
a time, the selection is done by requesting change of a child pin state
|
||||
on desired parent, with the use of ``DPLL_A_PIN_PARENT`` nested
|
||||
attribute. Example of netlink `set state on parent pin` message format:
|
||||
|
||||
========================== =============================================
|
||||
``DPLL_A_PIN_ID`` child pin id
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attribute for requesting configuration
|
||||
related to parent pin
|
||||
``DPLL_A_PIN_PARENT_ID`` parent pin id
|
||||
``DPLL_A_PIN_STATE`` requested pin state on parent
|
||||
========================== =============================================
|
||||
|
||||
Pin priority
|
||||
============
|
||||
|
||||
Some devices might offer a capability of automatic pin selection mode
|
||||
(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
|
||||
Usually, automatic selection is performed on the hardware level, which
|
||||
means only pins directly connected to the dpll can be used for automatic
|
||||
input pin selection.
|
||||
In automatic selection mode, the user cannot manually select a input
|
||||
pin for the device, instead the user shall provide all directly
|
||||
connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
|
||||
pick a highest priority valid signal and use it to control the DPLL
|
||||
device. Example of netlink `set priority on parent pin` message format:
|
||||
|
||||
============================ =============================================
|
||||
``DPLL_A_PIN_ID`` configured pin id
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting configuration
|
||||
related to parent dpll device
|
||||
``DPLL_A_PIN_PARENT_ID`` parent dpll device id
|
||||
``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
|
||||
============================ =============================================
|
||||
|
||||
Child pin of MUX-type pin is not capable of automatic input pin selection,
|
||||
in order to configure active input of a MUX-type pin, the user needs to
|
||||
request desired pin state of the child pin on the parent pin,
|
||||
as described in the ``MUX-type pins`` chapter.
|
||||
|
||||
Configuration commands group
|
||||
============================
|
||||
|
||||
Configuration commands are used to get information about registered
|
||||
dpll devices (and pins), as well as set configuration of device or pins.
|
||||
As dpll devices must be abstracted and reflect real hardware,
|
||||
there is no way to add new dpll device via netlink from user space and
|
||||
each device should be registered by its driver.
|
||||
|
||||
All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
|
||||
any spamming/DoS from unauthorized userspace applications.
|
||||
|
||||
List of netlink commands with possible attributes
|
||||
=================================================
|
||||
|
||||
Constants identifying command types for dpll device uses a
|
||||
``DPLL_CMD_`` prefix and suffix according to command purpose.
|
||||
The dpll device related attributes use a ``DPLL_A_`` prefix and
|
||||
suffix according to attribute purpose.
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_ID_GET`` command to get device ID
|
||||
``DPLL_A_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_TYPE`` attr type of dpll device
|
||||
==================================== =================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_GET`` command to get device info or
|
||||
dump list of available devices
|
||||
``DPLL_A_ID`` attr unique dpll device ID
|
||||
``DPLL_A_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_MODE`` attr selection mode
|
||||
``DPLL_A_MODE_SUPPORTED`` attr available selection modes
|
||||
``DPLL_A_LOCK_STATUS`` attr dpll device lock status
|
||||
``DPLL_A_TEMP`` attr device temperature info
|
||||
``DPLL_A_TYPE`` attr type of dpll device
|
||||
==================================== =================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_DEVICE_SET`` command to set dpll device config
|
||||
``DPLL_A_ID`` attr internal dpll device index
|
||||
``DPLL_A_MODE`` attr selection mode to configure
|
||||
==================================== =================================
|
||||
|
||||
Constants identifying command types for pins uses a
|
||||
``DPLL_CMD_PIN_`` prefix and suffix according to command purpose.
|
||||
The pin related attributes use a ``DPLL_A_PIN_`` prefix and suffix
|
||||
according to attribute purpose.
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_PIN_ID_GET`` command to get pin ID
|
||||
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_TYPE`` attr type of a pin
|
||||
==================================== =================================
|
||||
|
||||
==================================== ==================================
|
||||
``DPLL_CMD_PIN_GET`` command to get pin info or dump
|
||||
list of available pins
|
||||
``DPLL_A_PIN_ID`` attr unique a pin ID
|
||||
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
|
||||
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
|
||||
(EUI-64), as defined by the
|
||||
IEEE 1588 standard
|
||||
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
|
||||
by registerer
|
||||
``DPLL_A_PIN_TYPE`` attr type of a pin
|
||||
``DPLL_A_PIN_FREQUENCY`` attr current frequency of a pin
|
||||
``DPLL_A_PIN_FREQUENCY_SUPPORTED`` nested attr provides supported
|
||||
frequencies
|
||||
``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
|
||||
``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent device
|
||||
the pin is connected with
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
|
||||
``DPLL_A_PIN_PRIO`` attr priority of pin on the
|
||||
dpll device
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
dpll device
|
||||
``DPLL_A_PIN_DIRECTION`` attr direction of a pin on the
|
||||
parent dpll device
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
|
||||
the pin is connected with
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
pin
|
||||
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
||||
==================================== ==================================
|
||||
|
||||
==================================== =================================
|
||||
``DPLL_CMD_PIN_SET`` command to set pins configuration
|
||||
``DPLL_A_PIN_ID`` attr unique a pin ID
|
||||
``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent dpll
|
||||
device configuration request
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
|
||||
``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
|
||||
``DPLL_A_PIN_PRIO`` attr requested priority of pin on
|
||||
the dpll device
|
||||
``DPLL_A_PIN_STATE`` attr requested state of pin on
|
||||
the dpll device
|
||||
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
|
||||
configuration request
|
||||
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
|
||||
``DPLL_A_PIN_STATE`` attr requested state of pin on
|
||||
parent pin
|
||||
==================================== =================================
|
||||
|
||||
Netlink dump requests
|
||||
=====================
|
||||
|
||||
The ``DPLL_CMD_DEVICE_GET`` and ``DPLL_CMD_PIN_GET`` commands are
|
||||
capable of dump type netlink requests, in which case the response is in
|
||||
the same format as for their ``do`` request, but every device or pin
|
||||
registered in the system is returned.
|
||||
|
||||
SET commands format
|
||||
===================
|
||||
|
||||
``DPLL_CMD_DEVICE_SET`` - to target a dpll device, the user provides
|
||||
``DPLL_A_ID``, which is unique identifier of dpll device in the system,
|
||||
as well as parameter being configured (``DPLL_A_MODE``).
|
||||
|
||||
``DPLL_CMD_PIN_SET`` - to target a pin user must provide a
|
||||
``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
|
||||
Also configured pin parameters must be added.
|
||||
If ``DPLL_A_PIN_FREQUENCY`` is configured, this affects all the dpll
|
||||
devices that are connected with the pin, that is why frequency attribute
|
||||
shall not be enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
|
||||
Other attributes: ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE`` or
|
||||
``DPLL_A_PIN_DIRECTION`` must be enclosed in
|
||||
``DPLL_A_PIN_PARENT_DEVICE`` as their configuration relates to only one
|
||||
of parent dplls, targeted by ``DPLL_A_PIN_PARENT_ID`` attribute which is
|
||||
also required inside that nest.
|
||||
For MUX-type pins the ``DPLL_A_PIN_STATE`` attribute is configured in
|
||||
similar way, by enclosing required state in ``DPLL_A_PIN_PARENT_PIN``
|
||||
nested attribute and targeted parent pin id in ``DPLL_A_PIN_PARENT_ID``.
|
||||
|
||||
In general, it is possible to configure multiple parameters at once, but
|
||||
internally each parameter change will be invoked separately, where order
|
||||
of configuration is not guaranteed by any means.
|
||||
|
||||
Configuration pre-defined enums
|
||||
===============================
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/dpll.h
|
||||
|
||||
Notifications
|
||||
=============
|
||||
|
||||
dpll device can provide notifications regarding status changes of the
|
||||
device, i.e. lock status changes, input/output changes or other alarms.
|
||||
There is one multicast group that is used to notify user-space apps via
|
||||
netlink socket: ``DPLL_MCGRP_MONITOR``
|
||||
|
||||
Notifications messages:
|
||||
|
||||
============================== =====================================
|
||||
``DPLL_CMD_DEVICE_CREATE_NTF`` dpll device was created
|
||||
``DPLL_CMD_DEVICE_DELETE_NTF`` dpll device was deleted
|
||||
``DPLL_CMD_DEVICE_CHANGE_NTF`` dpll device has changed
|
||||
``DPLL_CMD_PIN_CREATE_NTF`` dpll pin was created
|
||||
``DPLL_CMD_PIN_DELETE_NTF`` dpll pin was deleted
|
||||
``DPLL_CMD_PIN_CHANGE_NTF`` dpll pin has changed
|
||||
============================== =====================================
|
||||
|
||||
Events format is the same as for the corresponding get command.
|
||||
Format of ``DPLL_CMD_DEVICE_`` events is the same as response of
|
||||
``DPLL_CMD_DEVICE_GET``.
|
||||
Format of ``DPLL_CMD_PIN_`` events is same as response of
|
||||
``DPLL_CMD_PIN_GET``.
|
||||
|
||||
Device driver implementation
|
||||
============================
|
||||
|
||||
Device is allocated by dpll_device_get() call. Second call with the
|
||||
same arguments will not create new object but provides pointer to
|
||||
previously created device for given arguments, it also increases
|
||||
refcount of that object.
|
||||
Device is deallocated by dpll_device_put() call, which first
|
||||
decreases the refcount, once refcount is cleared the object is
|
||||
destroyed.
|
||||
|
||||
Device should implement set of operations and register device via
|
||||
dpll_device_register() at which point it becomes available to the
|
||||
users. Multiple driver instances can obtain reference to it with
|
||||
dpll_device_get(), as well as register dpll device with their own
|
||||
ops and priv.
|
||||
|
||||
The pins are allocated separately with dpll_pin_get(), it works
|
||||
similarly to dpll_device_get(). Function first creates object and then
|
||||
for each call with the same arguments only the object refcount
|
||||
increases. Also dpll_pin_put() works similarly to dpll_device_put().
|
||||
|
||||
A pin can be registered with parent dpll device or parent pin, depending
|
||||
on hardware needs. Each registration requires registerer to provide set
|
||||
of pin callbacks, and private data pointer for calling them:
|
||||
|
||||
- dpll_pin_register() - register pin with a dpll device,
|
||||
- dpll_pin_on_pin_register() - register pin with another MUX type pin.
|
||||
|
||||
Notifications of adding or removing dpll devices are created within
|
||||
subsystem itself.
|
||||
Notifications about registering/deregistering pins are also invoked by
|
||||
the subsystem.
|
||||
Notifications about status changes either of dpll device or a pin are
|
||||
invoked in two ways:
|
||||
|
||||
- after successful change was requested on dpll subsystem, the subsystem
|
||||
calls corresponding notification,
|
||||
- requested by device driver with dpll_device_change_ntf() or
|
||||
dpll_pin_change_ntf() when driver informs about the status change.
|
||||
|
||||
The device driver using dpll interface is not required to implement all
|
||||
the callback operation. Nevertheless, there are few required to be
|
||||
implemented.
|
||||
Required dpll device level callback operations:
|
||||
|
||||
- ``.mode_get``,
|
||||
- ``.lock_status_get``.
|
||||
|
||||
Required pin level callback operations:
|
||||
|
||||
- ``.state_on_dpll_get`` (pins registered with dpll device),
|
||||
- ``.state_on_pin_get`` (pins registered with parent pin),
|
||||
- ``.direction_get``.
|
||||
|
||||
Every other operation handler is checked for existence and
|
||||
``-EOPNOTSUPP`` is returned in case of absence of specific handler.
|
||||
|
||||
The simplest implementation is in the OCP TimeCard driver. The ops
|
||||
structures are defined like this:
|
||||
|
||||
.. code-block:: c
|
||||
static const struct dpll_device_ops dpll_ops = {
|
||||
.lock_status_get = ptp_ocp_dpll_lock_status_get,
|
||||
.mode_get = ptp_ocp_dpll_mode_get,
|
||||
.mode_supported = ptp_ocp_dpll_mode_supported,
|
||||
};
|
||||
|
||||
static const struct dpll_pin_ops dpll_pins_ops = {
|
||||
.frequency_get = ptp_ocp_dpll_frequency_get,
|
||||
.frequency_set = ptp_ocp_dpll_frequency_set,
|
||||
.direction_get = ptp_ocp_dpll_direction_get,
|
||||
.direction_set = ptp_ocp_dpll_direction_set,
|
||||
.state_on_dpll_get = ptp_ocp_dpll_state_get,
|
||||
};
|
||||
|
||||
The registration part is then looks like this part:
|
||||
|
||||
.. code-block:: c
|
||||
clkid = pci_get_dsn(pdev);
|
||||
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
|
||||
if (IS_ERR(bp->dpll)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < OCP_SMA_NUM; i++) {
|
||||
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
|
||||
if (IS_ERR(bp->sma[i].dpll_pin)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
goto out_dpll;
|
||||
}
|
||||
|
||||
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
|
||||
&bp->sma[i]);
|
||||
if (err) {
|
||||
dpll_pin_put(bp->sma[i].dpll_pin);
|
||||
goto out_dpll;
|
||||
}
|
||||
}
|
||||
|
||||
In the error path we have to rewind every allocation in the reverse order:
|
||||
|
||||
.. code-block:: c
|
||||
while (i) {
|
||||
--i;
|
||||
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
|
||||
dpll_pin_put(bp->sma[i].dpll_pin);
|
||||
}
|
||||
dpll_device_put(bp->dpll);
|
||||
|
||||
More complex example can be found in Intel's ICE driver or nVidia's mlx5 driver.
|
||||
|
||||
SyncE enablement
|
||||
================
|
||||
For SyncE enablement it is required to allow control over dpll device
|
||||
for a software application which monitors and configures the inputs of
|
||||
dpll device in response to current state of a dpll device and its
|
||||
inputs.
|
||||
In such scenario, dpll device input signal shall be also configurable
|
||||
to drive dpll with signal recovered from the PHY netdevice.
|
||||
This is done by exposing a pin to the netdevice - attaching pin to the
|
||||
netdevice itself with
|
||||
``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``.
|
||||
|
|
@ -114,6 +114,7 @@ available subsections can be seen below.
|
|||
zorro
|
||||
hte/index
|
||||
wmi
|
||||
dpll
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
|
|
|||
488
Documentation/netlink/specs/dpll.yaml
Normal file
488
Documentation/netlink/specs/dpll.yaml
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
# 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
|
||||
render-max: true
|
||||
-
|
||||
type: enum
|
||||
name: lock-status
|
||||
doc: |
|
||||
provides information of dpll device lock status, valid values for
|
||||
DPLL_A_LOCK_STATUS attribute
|
||||
entries:
|
||||
-
|
||||
name: unlocked
|
||||
doc: |
|
||||
dpll was not yet locked to any valid input (or forced by setting
|
||||
DPLL_A_MODE to DPLL_MODE_DETACHED)
|
||||
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-capabilities
|
||||
doc: |
|
||||
defines possible capabilities of a pin, valid flags on
|
||||
DPLL_A_PIN_CAPABILITIES attribute
|
||||
entries:
|
||||
-
|
||||
name: direction-can-change
|
||||
doc: pin direction can be changed
|
||||
-
|
||||
name: priority-can-change
|
||||
doc: pin priority 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
|
||||
-
|
||||
name: module-name
|
||||
type: string
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: clock-id
|
||||
type: u64
|
||||
-
|
||||
name: mode
|
||||
type: u32
|
||||
enum: mode
|
||||
-
|
||||
name: mode-supported
|
||||
type: u32
|
||||
enum: mode
|
||||
multi-attr: true
|
||||
-
|
||||
name: lock-status
|
||||
type: u32
|
||||
enum: lock-status
|
||||
-
|
||||
name: temp
|
||||
type: s32
|
||||
-
|
||||
name: type
|
||||
type: u32
|
||||
enum: type
|
||||
-
|
||||
name: pin
|
||||
enum-name: dpll_a_pin
|
||||
attributes:
|
||||
-
|
||||
name: id
|
||||
type: u32
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: module-name
|
||||
type: string
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: clock-id
|
||||
type: u64
|
||||
-
|
||||
name: board-label
|
||||
type: string
|
||||
-
|
||||
name: panel-label
|
||||
type: string
|
||||
-
|
||||
name: package-label
|
||||
type: string
|
||||
-
|
||||
name: type
|
||||
type: u32
|
||||
enum: pin-type
|
||||
-
|
||||
name: direction
|
||||
type: u32
|
||||
enum: pin-direction
|
||||
-
|
||||
name: frequency
|
||||
type: u64
|
||||
-
|
||||
name: frequency-supported
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: frequency-range
|
||||
-
|
||||
name: frequency-min
|
||||
type: u64
|
||||
-
|
||||
name: frequency-max
|
||||
type: u64
|
||||
-
|
||||
name: prio
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
enum: pin-state
|
||||
-
|
||||
name: capabilities
|
||||
type: u32
|
||||
-
|
||||
name: parent-device
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: pin-parent-device
|
||||
-
|
||||
name: parent-pin
|
||||
type: nest
|
||||
multi-attr: true
|
||||
nested-attributes: pin-parent-pin
|
||||
-
|
||||
name: pin-parent-device
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: direction
|
||||
type: u32
|
||||
-
|
||||
name: prio
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
-
|
||||
name: pin-parent-pin
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: parent-id
|
||||
type: u32
|
||||
-
|
||||
name: state
|
||||
type: u32
|
||||
-
|
||||
name: frequency-range
|
||||
subset-of: pin
|
||||
attributes:
|
||||
-
|
||||
name: frequency-min
|
||||
type: u64
|
||||
-
|
||||
name: frequency-max
|
||||
type: u64
|
||||
|
||||
operations:
|
||||
enum-name: dpll_cmd
|
||||
list:
|
||||
-
|
||||
name: device-id-get
|
||||
doc: |
|
||||
Get id of dpll device 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
|
||||
- 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
|
||||
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
|
||||
-
|
||||
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: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-lock-doit
|
||||
post: dpll-unlock-doit
|
||||
request:
|
||||
attributes:
|
||||
- module-name
|
||||
- clock-id
|
||||
- board-label
|
||||
- panel-label
|
||||
- package-label
|
||||
- type
|
||||
reply:
|
||||
attributes:
|
||||
- id
|
||||
|
||||
-
|
||||
name: pin-get
|
||||
doc: |
|
||||
Get list of pins and its attributes.
|
||||
- dump request without any attributes given - list all the pins in the
|
||||
system
|
||||
- dump request with target dpll - list all the pins registered with
|
||||
a given dpll device
|
||||
- do request with target dpll and target pin - single pin attributes
|
||||
attribute-set: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pin-pre-doit
|
||||
post: dpll-pin-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
reply: &pin-attrs
|
||||
attributes:
|
||||
- id
|
||||
- board-label
|
||||
- panel-label
|
||||
- package-label
|
||||
- type
|
||||
- frequency
|
||||
- frequency-supported
|
||||
- capabilities
|
||||
- parent-device
|
||||
- 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: pin
|
||||
flags: [ admin-perm ]
|
||||
|
||||
do:
|
||||
pre: dpll-pin-pre-doit
|
||||
post: dpll-pin-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- id
|
||||
- frequency
|
||||
- direction
|
||||
- prio
|
||||
- state
|
||||
- parent-device
|
||||
- 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
|
||||
11
MAINTAINERS
11
MAINTAINERS
|
|
@ -6333,6 +6333,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>
|
||||
|
|
|
|||
|
|
@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
|
|||
|
||||
source "drivers/cdx/Kconfig"
|
||||
|
||||
source "drivers/dpll/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -197,5 +197,6 @@ obj-$(CONFIG_PECI) += peci/
|
|||
obj-$(CONFIG_HTE) += hte/
|
||||
obj-$(CONFIG_DRM_ACCEL) += accel/
|
||||
obj-$(CONFIG_CDX_BUS) += cdx/
|
||||
obj-$(CONFIG_DPLL) += dpll/
|
||||
|
||||
obj-$(CONFIG_S390) += s390/
|
||||
|
|
|
|||
7
drivers/dpll/Kconfig
Normal file
7
drivers/dpll/Kconfig
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Generic DPLL drivers configuration
|
||||
#
|
||||
|
||||
config DPLL
|
||||
bool
|
||||
9
drivers/dpll/Makefile
Normal file
9
drivers/dpll/Makefile
Normal file
|
|
@ -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
|
||||
798
drivers/dpll/dpll_core.c
Normal file
798
drivers/dpll/dpll_core.c
Normal file
|
|
@ -0,0 +1,798 @@
|
|||
// 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"
|
||||
#include "dpll_netlink.h"
|
||||
|
||||
/* Mutex lock to protect DPLL subsystem devices and pins */
|
||||
DEFINE_MUTEX(dpll_lock);
|
||||
|
||||
DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
|
||||
DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
|
||||
|
||||
static u32 dpll_xa_id;
|
||||
|
||||
#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(®->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(®->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->id, 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->id);
|
||||
kfree(ref);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
reg->ops = ops;
|
||||
reg->priv = priv;
|
||||
if (ref_exists)
|
||||
refcount_inc(&ref->refcount);
|
||||
list_add_tail(®->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(®->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_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_cyclic(&dpll_device_xa, &dpll->id, dpll, xa_limit_32b,
|
||||
&dpll_xa_id, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
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(®->list, &dpll->registration_list);
|
||||
if (!first_registration) {
|
||||
mutex_unlock(&dpll_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
|
||||
dpll_device_create_ntf(dpll);
|
||||
mutex_unlock(&dpll_lock);
|
||||
|
||||
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(®->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_unregister(ref->dpll, pin, ops, priv);
|
||||
dpll_pin_delete_ntf(pin);
|
||||
}
|
||||
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 = genl_register_family(&dpll_nl_family);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mutex_destroy(&dpll_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dpll_exit(void)
|
||||
{
|
||||
genl_unregister_family(&dpll_nl_family);
|
||||
mutex_destroy(&dpll_lock);
|
||||
}
|
||||
|
||||
subsys_initcall(dpll_init);
|
||||
module_exit(dpll_exit);
|
||||
89
drivers/dpll/dpll_core.h
Normal file
89
drivers/dpll/dpll_core.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* 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_nl.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;
|
||||
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
|
||||
1253
drivers/dpll/dpll_netlink.c
Normal file
1253
drivers/dpll/dpll_netlink.c
Normal file
File diff suppressed because it is too large
Load Diff
13
drivers/dpll/dpll_netlink.h
Normal file
13
drivers/dpll/dpll_netlink.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* 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);
|
||||
162
drivers/dpll/dpll_nl.c
Normal file
162
drivers/dpll/dpll_nl.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// 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_PIN_PARENT_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
};
|
||||
|
||||
const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
|
||||
[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
};
|
||||
|
||||
/* 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_U32, 1, 2),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_GET - do */
|
||||
static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_SET - do */
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_ID + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_ID_GET - do */
|
||||
static const struct nla_policy dpll_pin_id_get_nl_policy[DPLL_A_PIN_TYPE + 1] = {
|
||||
[DPLL_A_PIN_MODULE_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DPLL_A_PIN_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_U32, 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_PIN_ID + 1] = {
|
||||
[DPLL_A_PIN_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_U32, 1, 2),
|
||||
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 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_ID,
|
||||
.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_ID,
|
||||
.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_PIN_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),
|
||||
};
|
||||
51
drivers/dpll/dpll_nl.h
Normal file
51
drivers/dpll/dpll_nl.h
Normal file
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#include "ice_vsi_vlan_ops.h"
|
||||
#include "ice_gnss.h"
|
||||
#include "ice_irq.h"
|
||||
#include "ice_dpll.h"
|
||||
|
||||
#define ICE_BAR0 0
|
||||
#define ICE_REQ_DESC_MULTIPLE 32
|
||||
|
|
@ -198,7 +199,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_ROCE_LAG,
|
||||
ICE_F_SRIOV_LAG,
|
||||
|
|
@ -508,6 +511,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 */
|
||||
};
|
||||
|
||||
|
|
@ -640,6 +644,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;
|
||||
};
|
||||
|
||||
extern struct workqueue_struct *ice_lag_wq;
|
||||
|
|
|
|||
|
|
@ -1351,6 +1351,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;
|
||||
|
|
@ -1367,6 +1391,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)
|
||||
|
|
@ -1403,8 +1429,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_C827 0x31
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575 0x21
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_ZL30632_80032 0x24
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_SI5383_5384 0x25
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_E822_PHY 0x30
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_C827 0x31
|
||||
#define ICE_AQC_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX 0x47
|
||||
u8 rsvd[9];
|
||||
};
|
||||
|
||||
|
|
@ -2125,6 +2155,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;
|
||||
|
|
@ -2194,6 +2411,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;
|
||||
|
|
@ -2234,6 +2453,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;
|
||||
|
|
@ -2358,6 +2586,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,
|
||||
|
|
@ -2413,6 +2643,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 */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "ice_ptp_hw.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",
|
||||
|
|
@ -435,6 +436,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
|
||||
|
|
@ -2654,33 +2730,6 @@ ice_parse_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_p,
|
|||
ice_recalc_port_limited_caps(hw, &dev_p->common_cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static 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 -EIO;
|
||||
|
||||
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_is_pf_c827 - check if pf contains c827 phy
|
||||
* @hw: pointer to the hw struct
|
||||
|
|
@ -4998,6 +5047,395 @@ ice_dis_vsi_rdma_qset(struct ice_port_info *pi, u16 count, u32 *qset_teid,
|
|||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_get_cgu_abilities - get cgu abilities
|
||||
* @hw: pointer to the HW struct
|
||||
* @abilities: CGU abilities
|
||||
*
|
||||
* Get CGU abilities (0x0C61)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - set input pin config
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - get input pin config
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - set output pin config
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - get output pin config
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_get_cgu_dpll_status - get dpll status
|
||||
* @hw: pointer to the HW struct
|
||||
* @dpll_num: DPLL index
|
||||
* @ref_state: Reference clock state
|
||||
* @config: current DPLL config
|
||||
* @dpll_state: current DPLL state
|
||||
* @phase_offset: Phase offset in ns
|
||||
* @eec_mode: EEC_mode
|
||||
*
|
||||
* Get CGU DPLL status (0x0C66)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 = div64_s64(sign_extend64(*phase_offset, 47),
|
||||
nsec_per_psec);
|
||||
*eec_mode = cmd->eec_mode;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_set_cgu_dpll_config - set 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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - set input reference priority
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - get input reference priority
|
||||
* @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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 - 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)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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 phy recovered clock as reference (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 - get phy recovered signal info
|
||||
* @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 info (0x0631)
|
||||
* Return: 0 on success or negative value on failure.
|
||||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
|
|||
struct ice_sq_cd *cd);
|
||||
bool ice_is_pf_c827(struct ice_hw *hw);
|
||||
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
|
||||
|
|
@ -196,6 +202,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);
|
||||
|
|
|
|||
1904
drivers/net/ethernet/intel/ice/ice_dpll.c
Normal file
1904
drivers/net/ethernet/intel/ice/ice_dpll.c
Normal file
File diff suppressed because it is too large
Load Diff
104
drivers/net/ethernet/intel/ice/ice_dpll.h
Normal file
104
drivers/net/ethernet/intel/ice/ice_dpll.h
Normal file
|
|
@ -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 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;
|
||||
};
|
||||
|
||||
/** 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
|
||||
|
|
@ -3985,13 +3985,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;
|
||||
|
|
|
|||
|
|
@ -4665,6 +4665,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");
|
||||
|
|
@ -4691,6 +4695,8 @@ 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 (test_bit(ICE_FLAG_DPLL, pf->flags))
|
||||
ice_dpll_deinit(pf);
|
||||
}
|
||||
|
||||
static void ice_init_wakeup(struct ice_pf *pf)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,132 @@
|
|||
#include "ice_ptp_consts.h"
|
||||
#include "ice_cgu_regs.h"
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
/* Low level functions for interacting with and managing the device clock used
|
||||
* for the Precision Time Protocol.
|
||||
*
|
||||
|
|
@ -3353,6 +3479,90 @@ int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx)
|
|||
return ice_clear_phy_tstamp_e822(hw, block, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_is_phy_rclk_present - check recovered clk presence
|
||||
* @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_AQC_GET_LINK_TOPO_NODE_NR_C827, NULL) &&
|
||||
ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
|
||||
ICE_AQC_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_AQC_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_AQC_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_ptp_reset_ts_memory - Reset timestamp memory for all blocks
|
||||
* @hw: pointer to the HW struct
|
||||
|
|
@ -3407,3 +3617,323 @@ 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 - check for CGU presence
|
||||
* @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_AQC_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
|
||||
NULL)) {
|
||||
hw->cgu_part_number = ICE_AQC_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_AQC_GET_LINK_TOPO_NODE_NR_SI5383_5384,
|
||||
NULL)) {
|
||||
hw->cgu_part_number = ICE_AQC_GET_LINK_TOPO_NODE_NR_SI5383_5384;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_cgu_get_pin_desc_e823 - get pin description array
|
||||
* @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_AQC_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_AQC_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 - get pin description array
|
||||
* @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 - get pin's 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 - get pin's supported frequency
|
||||
* @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 - get pin's 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
|
||||
* @eec_mode: eec mode of the DPLL
|
||||
* @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)
|
||||
{
|
||||
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)
|
||||
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 0;
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_AQC_GET_LINK_TOPO_NODE_NR_ZL30632_80032)
|
||||
*base_idx = ZL_REF1P;
|
||||
else if (hw->cgu_part_number ==
|
||||
ICE_AQC_GET_LINK_TOPO_NODE_NR_SI5383_5384)
|
||||
*base_idx = SI_REF1P;
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -110,6 +111,77 @@ 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_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
|
||||
};
|
||||
|
||||
struct ice_cgu_pin_desc {
|
||||
char *name;
|
||||
u8 index;
|
||||
enum dpll_pin_type type;
|
||||
u32 freq_supp_num;
|
||||
struct dpll_pin_frequency *freq_supp;
|
||||
};
|
||||
|
||||
extern const struct
|
||||
ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
|
||||
|
||||
|
|
@ -197,6 +269,20 @@ int ice_ptp_init_phy_e810(struct ice_hw *hw);
|
|||
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);
|
||||
int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);
|
||||
|
||||
#define PFTSYN_SEM_BYTES 4
|
||||
|
||||
|
|
|
|||
|
|
@ -965,6 +965,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 */
|
||||
|
|
|
|||
|
|
@ -189,3 +189,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.
|
||||
|
||||
|
|
|
|||
|
|
@ -128,3 +128,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
|
||||
|
|
|
|||
|
|
@ -206,6 +206,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,
|
||||
|
|
@ -215,6 +228,8 @@ enum {
|
|||
MLX5_INTERFACE_PROTOCOL_MPIB,
|
||||
|
||||
MLX5_INTERFACE_PROTOCOL_VNET,
|
||||
|
||||
MLX5_INTERFACE_PROTOCOL_DPLL,
|
||||
};
|
||||
|
||||
static const struct mlx5_adev_device {
|
||||
|
|
@ -237,6 +252,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)
|
||||
|
|
|
|||
432
drivers/net/ethernet/mellanox/mlx5/core/dpll.c
Normal file
432
drivers/net/ethernet/mellanox/mlx5/core/dpll.c
Normal file
|
|
@ -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_CAPABILITIES_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");
|
||||
|
|
@ -188,6 +188,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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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_CAPABILITIES_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_CAPABILITIES_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,16 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
|
|||
bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */
|
||||
bp->sma[3].default_fcn = 0x02; /* OUT: PHC */
|
||||
|
||||
/* If no SMA map, the pin functions and directions are fixed. */
|
||||
if (!bp->art_sma) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (i = 0; i < OCP_SMA_NUM; i++) {
|
||||
/* If no SMA map, the pin functions and directions are fixed. */
|
||||
bp->sma[i].dpll_prop = prop;
|
||||
bp->sma[i].dpll_prop.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 +2521,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_CAPABILITIES_DIRECTION_CAN_CHANGE;
|
||||
break;
|
||||
default:
|
||||
bp->sma[i].mode = SMA_MODE_OUT;
|
||||
bp->sma[i].dpll_prop.capabilities =
|
||||
DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2555,6 +2594,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 +2738,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 +2775,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 +3883,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 +4116,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 +4124,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 +4224,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 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, i, 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 +4404,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 +4437,43 @@ 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)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < OCP_SMA_NUM; i++) {
|
||||
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
|
||||
if (IS_ERR(bp->sma[i].dpll_pin)) {
|
||||
err = PTR_ERR(bp->dpll);
|
||||
goto out_dpll;
|
||||
}
|
||||
|
||||
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
|
||||
&bp->sma[i]);
|
||||
if (err) {
|
||||
dpll_pin_put(bp->sma[i].dpll_pin);
|
||||
goto out_dpll;
|
||||
}
|
||||
}
|
||||
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 +4488,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);
|
||||
|
|
|
|||
152
include/linux/dpll.h
Normal file
152
include/linux/dpll.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/* 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);
|
||||
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;
|
||||
};
|
||||
|
||||
#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);
|
||||
|
||||
void dpll_device_put(struct dpll_device *dpll);
|
||||
|
||||
int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
|
||||
const struct dpll_device_ops *ops, void *priv);
|
||||
|
||||
void dpll_device_unregister(struct dpll_device *dpll,
|
||||
const struct dpll_device_ops *ops, void *priv);
|
||||
|
||||
struct dpll_pin *
|
||||
dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
|
||||
const struct dpll_pin_properties *prop);
|
||||
|
||||
int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv);
|
||||
|
||||
void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv);
|
||||
|
||||
void dpll_pin_put(struct dpll_pin *pin);
|
||||
|
||||
int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv);
|
||||
|
||||
void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
|
||||
const struct dpll_pin_ops *ops, void *priv);
|
||||
|
||||
int dpll_device_change_ntf(struct dpll_device *dpll);
|
||||
|
||||
int dpll_pin_change_ntf(struct dpll_pin *pin);
|
||||
|
||||
#endif
|
||||
|
|
@ -155,6 +155,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,
|
||||
|
|
|
|||
|
|
@ -10176,7 +10176,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];
|
||||
|
||||
|
|
@ -12549,4 +12551,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 */
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ struct xdp_buff;
|
|||
struct xdp_frame;
|
||||
struct xdp_metadata_ops;
|
||||
struct xdp_md;
|
||||
/* DPLL specific */
|
||||
struct dpll_pin;
|
||||
|
||||
typedef u32 xdp_features_t;
|
||||
|
||||
|
|
@ -2049,6 +2051,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.
|
||||
*/
|
||||
|
|
@ -2405,6 +2410,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)
|
||||
|
||||
|
|
@ -3940,6 +3949,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);
|
||||
|
|
|
|||
201
include/uapi/linux/dpll.h
Normal file
201
include/uapi/linux/dpll.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* 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
|
||||
*/
|
||||
enum dpll_mode {
|
||||
DPLL_MODE_MANUAL = 1,
|
||||
DPLL_MODE_AUTOMATIC,
|
||||
|
||||
/* private: */
|
||||
__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
|
||||
* forced by setting DPLL_A_MODE to DPLL_MODE_DETACHED)
|
||||
* @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,
|
||||
|
||||
/* private: */
|
||||
__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,
|
||||
|
||||
/* private: */
|
||||
__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,
|
||||
|
||||
/* private: */
|
||||
__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,
|
||||
|
||||
/* private: */
|
||||
__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,
|
||||
|
||||
/* private: */
|
||||
__DPLL_PIN_STATE_MAX,
|
||||
DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dpll_pin_capabilities - defines possible capabilities of a pin, valid
|
||||
* flags on DPLL_A_PIN_CAPABILITIES attribute
|
||||
* @DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE: pin direction can be changed
|
||||
* @DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE: pin priority can be changed
|
||||
* @DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE: pin state can be changed
|
||||
*/
|
||||
enum dpll_pin_capabilities {
|
||||
DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE = 1,
|
||||
DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE = 2,
|
||||
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
|
||||
};
|
||||
|
||||
enum dpll_a {
|
||||
DPLL_A_ID = 1,
|
||||
DPLL_A_MODULE_NAME,
|
||||
DPLL_A_PAD,
|
||||
DPLL_A_CLOCK_ID,
|
||||
DPLL_A_MODE,
|
||||
DPLL_A_MODE_SUPPORTED,
|
||||
DPLL_A_LOCK_STATUS,
|
||||
DPLL_A_TEMP,
|
||||
DPLL_A_TYPE,
|
||||
|
||||
__DPLL_A_MAX,
|
||||
DPLL_A_MAX = (__DPLL_A_MAX - 1)
|
||||
};
|
||||
|
||||
enum dpll_a_pin {
|
||||
DPLL_A_PIN_ID = 1,
|
||||
DPLL_A_PIN_PARENT_ID,
|
||||
DPLL_A_PIN_MODULE_NAME,
|
||||
DPLL_A_PIN_PAD,
|
||||
DPLL_A_PIN_CLOCK_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_CAPABILITIES,
|
||||
DPLL_A_PIN_PARENT_DEVICE,
|
||||
DPLL_A_PIN_PARENT_PIN,
|
||||
|
||||
__DPLL_A_PIN_MAX,
|
||||
DPLL_A_PIN_MAX = (__DPLL_A_PIN_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 */
|
||||
|
|
@ -376,7 +376,7 @@ enum {
|
|||
|
||||
IFLA_GSO_IPV4_MAX_SIZE,
|
||||
IFLA_GRO_IPV4_MAX_SIZE,
|
||||
|
||||
IFLA_DPLL_PIN,
|
||||
__IFLA_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9023,6 +9023,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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
#include <net/addrconf.h>
|
||||
#endif
|
||||
#include <linux/dpll.h>
|
||||
|
||||
#include "dev.h"
|
||||
|
||||
|
|
@ -1055,6 +1056,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 +1121,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 +1785,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 +1949,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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user