Subject: Re: [PATCH iwl-net 04/10] idpf: negotiate PTP capabilies and get PTP clock

From: Vadim Fedorenko <[email-masked]>
Date: Thu, 14 Nov 2024 12:17:06 +0000

On 13/11/2024 15:46, Milena Olech wrote:
> PTP capabilities are negotiated using virtchnl command. Add get
> capabilities function, direct access to read the PTP clock time and
> direct access to read the cross timestamp - system time and PTP clock
> time. Set initial PTP capabilities exposed to the stack.
> 
> Reviewed-by: Alexander Lobakin <[email-masked]>
> Signed-off-by: Milena Olech <[email-masked]>
> ---
>   drivers/net/ethernet/intel/idpf/Makefile      |   2 +
>   drivers/net/ethernet/intel/idpf/idpf.h        |   2 +
>   drivers/net/ethernet/intel/idpf/idpf_dev.c    |  14 +
>   .../ethernet/intel/idpf/idpf_lan_pf_regs.h    |   4 +
>   drivers/net/ethernet/intel/idpf/idpf_ptp.c    | 263 ++++++++++++++++++
>   drivers/net/ethernet/intel/idpf/idpf_ptp.h    |  85 ++++++
>   .../ethernet/intel/idpf/idpf_virtchnl_ptp.c   |  95 +++++++
>   7 files changed, 465 insertions(+)
>   create mode 100644 drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
> 
> diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile
> index 1f38a9d7125c..83ac5e296382 100644
> --- a/drivers/net/ethernet/intel/idpf/Makefile
> +++ b/drivers/net/ethernet/intel/idpf/Makefile
> @@ -17,4 +17,6 @@ idpf-y := \
>   	idpf_vf_dev.o
>   
>   idpf-$(CONFIG_IDPF_SINGLEQ)	+= idpf_singleq_txrx.o
> +
>   idpf-$(CONFIG_PTP_1588_CLOCK)	+= idpf_ptp.o
> +idpf-$(CONFIG_PTP_1588_CLOCK)	+= idpf_virtchnl_ptp.o
> diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
> index 2e8b14dd9d96..d5d5064d313b 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf.h
> +++ b/drivers/net/ethernet/intel/idpf/idpf.h
> @@ -189,6 +189,7 @@ struct idpf_vport_max_q {
>    * @mb_intr_reg_init: Mailbox interrupt register initialization
>    * @reset_reg_init: Reset register initialization
>    * @trigger_reset: Trigger a reset to occur
> + * @ptp_reg_init: PTP register initialization
>    */
>   struct idpf_reg_ops {
>   	void (*ctlq_reg_init)(struct idpf_ctlq_create_info *cq);
> @@ -197,6 +198,7 @@ struct idpf_reg_ops {
>   	void (*reset_reg_init)(struct idpf_adapter *adapter);
>   	void (*trigger_reset)(struct idpf_adapter *adapter,
>   			      enum idpf_flags trig_cause);
> +	void (*ptp_reg_init)(const struct idpf_adapter *adapter);
>   };
>   
>   /**
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c
> index 6c913a703df6..149f2c0afe92 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_dev.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c
> @@ -4,6 +4,7 @@
>   #include "idpf.h"
>   #include "idpf_lan_pf_regs.h"
>   #include "idpf_virtchnl.h"
> +#include "idpf_ptp.h"
>   
>   #define IDPF_PF_ITR_IDX_SPACING		0x4
>   
> @@ -145,6 +146,18 @@ static void idpf_trigger_reset(struct idpf_adapter *adapter,
>   	       idpf_get_reg_addr(adapter, PFGEN_CTRL));
>   }
>   
> +/**
> + * idpf_ptp_reg_init - Initialize required registers
> + * @adapter: Driver specific private structure
> + *
> + * Set the bits required for enabling shtime and cmd execution
> + */
> +static void idpf_ptp_reg_init(const struct idpf_adapter *adapter)
> +{
> +	adapter->ptp->cmd.shtime_enable_mask = PF_GLTSYN_CMD_SYNC_SHTIME_EN_M;
> +	adapter->ptp->cmd.exec_cmd_mask = PF_GLTSYN_CMD_SYNC_EXEC_CMD_M;
> +}
> +
>   /**
>    * idpf_reg_ops_init - Initialize register API function pointers
>    * @adapter: Driver specific private structure
> @@ -156,6 +169,7 @@ static void idpf_reg_ops_init(struct idpf_adapter *adapter)
>   	adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_mb_intr_reg_init;
>   	adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
>   	adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;
> +	adapter->dev_ops.reg_ops.ptp_reg_init = idpf_ptp_reg_init;
>   }
>   
>   /**
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h b/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
> index 24edb8a6ec2e..cc9aa2b6a14a 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
> +++ b/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
> @@ -53,6 +53,10 @@
>   #define PF_FW_ATQH_ATQH_M		GENMASK(9, 0)
>   #define PF_FW_ATQT			(PF_FW_BASE + 0x24)
>   
> +/* Timesync registers */
> +#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M	GENMASK(1, 0)
> +#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M	BIT(2)
> +
>   /* Interrupts */
>   #define PF_GLINT_BASE			0x08900000
>   #define PF_GLINT_DYN_CTL(_INT)		(PF_GLINT_BASE + ((_INT) * 0x1000))
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> index 1ac6367f5989..ab242f7d72a9 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
> @@ -4,6 +4,258 @@
>   #include "idpf.h"
>   #include "idpf_ptp.h"
>   
> +/**
> + * idpf_ptp_get_access - Determine the access type of the PTP features
> + * @adapter: Driver specific private structure
> + * @direct: Capability that indicates the direct access
> + * @mailbox: Capability that indicates the mailbox access
> + *
> + * Return: the type of supported access for the PTP feature.
> + */
> +static enum idpf_ptp_access
> +idpf_ptp_get_access(const struct idpf_adapter *adapter, u32 direct, u32 mailbox)
> +{
> +	if (adapter->ptp->caps & direct)
> +		return IDPF_PTP_DIRECT;
> +	else if (adapter->ptp->caps & mailbox)
> +		return IDPF_PTP_MAILBOX;
> +	else
> +		return IDPF_PTP_NONE;
> +}
> +
> +/**
> + * idpf_ptp_get_features_access - Determine the access type of PTP features
> + * @adapter: Driver specific private structure
> + *
> + * Fulfill the adapter structure with type of the supported PTP features
> + * access.
> + */
> +void idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
> +{
> +	struct idpf_ptp *ptp = adapter->ptp;
> +	u32 direct, mailbox;
> +
> +	/* Get the device clock time */
> +	direct = VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME;
> +	mailbox = VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB;
> +	ptp->get_dev_clk_time_access = idpf_ptp_get_access(adapter,
> +							   direct,
> +							   mailbox);
> +
> +	/* Get the cross timestamp */
> +	direct = VIRTCHNL2_CAP_PTP_GET_CROSS_TIME;
> +	mailbox = VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB;
> +	ptp->get_cross_tstamp_access = idpf_ptp_get_access(adapter,
> +							   direct,
> +							   mailbox);
> +}
> +
> +/**
> + * idpf_ptp_enable_shtime - Enable shadow time and execute a command
> + * @adapter: Driver specific private structure
> + */
> +static void idpf_ptp_enable_shtime(struct idpf_adapter *adapter)
> +{
> +	u32 shtime_enable, exec_cmd;
> +
> +	/* Get offsets */
> +	shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
> +	exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
> +
> +	/* Set the shtime en and the sync field */
> +	writel(shtime_enable, adapter->ptp->dev_clk_regs.cmd_sync);
> +	writel(exec_cmd | shtime_enable, adapter->ptp->dev_clk_regs.cmd_sync);
> +}
> +
> +/**
> + * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
> + * @adapter: Driver specific private structure
> + * @sts: Optional parameter for holding a pair of system timestamps from
> + *	 the system clock. Will be ignored when NULL is given.
> + *
> + * Return: the device clock time on success, -errno otherwise.
> + */
> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
> +					    struct ptp_system_timestamp *sts)
> +{
> +	struct idpf_ptp *ptp = adapter->ptp;
> +	u32 hi, lo;
> +
> +	/* Read the system timestamp pre PHC read */
> +	ptp_read_system_prets(sts);
> +
> +	idpf_ptp_enable_shtime(adapter);
> +	lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
> +
> +	/* Read the system timestamp post PHC read */
> +	ptp_read_system_postts(sts);
> +
> +	hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
> +
> +	return ((u64)hi << 32) | lo;
> +}

Am I right that idpf_ptp_enable_shtime() "freezes" the time in clk
registers and you can be sure that no changes will happen while you are
doing 2 transactions? If yes, then what does unfreeze it? Or does it
just copy new values to the registers and they will stay until the next
command?

> +
> +/**
> + * idpf_ptp_read_src_clk_reg - Read the main timer value
> + * @adapter: Driver specific private structure
> + * @src_clk: Returned main timer value in nanoseconds unit
> + * @sts: Optional parameter for holding a pair of system timestamps from
> + *	 the system clock. Will be ignored if NULL is given.
> + *
> + * Return: the device clock time on success, -errno otherwise.
> + */
> +static int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk,
> +				     struct ptp_system_timestamp *sts)
> +{
> +	switch (adapter->ptp->get_dev_clk_time_access) {
> +	case IDPF_PTP_NONE:
> +		return -EOPNOTSUPP;
> +	case IDPF_PTP_DIRECT:
> +		*src_clk = idpf_ptp_read_src_clk_reg_direct(adapter, sts);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER) || (IS_ENABLED(CONFIG_X86) && IS_ENABLED(CONFIG_PCIE_PTM))
> +/**
> + * idpf_ptp_get_sync_device_time_direct - Get the cross time stamp values
> + *					  directly
> + * @adapter: Driver specific private structure
> + * @dev_time: 64bit main timer value
> + * @sys_time: 64bit system time value
> + */
> +static void idpf_ptp_get_sync_device_time_direct(struct idpf_adapter *adapter,
> +						 u64 *dev_time, u64 *sys_time)
> +{
> +	u32 dev_time_lo, dev_time_hi, sys_time_lo, sys_time_hi;
> +	struct idpf_ptp *ptp = adapter->ptp;
> +
> +	idpf_ptp_enable_shtime(adapter);
> +
> +	dev_time_lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
> +	dev_time_hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
> +
> +	sys_time_lo = readl(ptp->dev_clk_regs.sys_time_ns_l);
> +	sys_time_hi = readl(ptp->dev_clk_regs.sys_time_ns_h);
> +
> +	*dev_time = ((u64)dev_time_hi << 32) | dev_time_lo;
> +	*sys_time = ((u64)sys_time_hi << 32) | sys_time_lo;
> +}
> +
> +/**
> + * idpf_ptp_get_sync_device_time - Get the cross time stamp info
> + * @device: Current device time
> + * @system: System counter value read synchronously with device time
> + * @ctx: Context provided by timekeeping code
> + *
> + * Return: the device and the system clocks time read simultaneously on success,
> + * -errno otherwise.
> + */
> +static int idpf_ptp_get_sync_device_time(ktime_t *device,
> +					 struct system_counterval_t *system,
> +					 void *ctx)
> +{
> +	struct idpf_adapter *adapter = ctx;
> +	u64 ns_time_dev, ns_time_sys;
> +
> +	switch (adapter->ptp->get_cross_tstamp_access) {
> +	case IDPF_PTP_NONE:
> +		return -EOPNOTSUPP;
> +	case IDPF_PTP_DIRECT:
> +		idpf_ptp_get_sync_device_time_direct(adapter, &ns_time_dev,
> +						     &ns_time_sys);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	*device = ns_to_ktime(ns_time_dev);
> +
> +#if IS_ENABLED(CONFIG_X86) && IS_ENABLED(CONFIG_PCIE_PTM)
> +	system->cycles = ns_time_sys;
> +	system->cs_id = CSID_X86_ART;
> +#endif /* CONFIG_X86 && CONFIG_PCIE_PTM */

Why don't you check if PTM is actually enabled for the device?

> +
> +	return 0;
> +}
> +
> +/**
> + * idpf_ptp_get_crosststamp - Capture a device cross timestamp
> + * @info: the driver's PTP info structure
> + * @cts: The memory to fill the cross timestamp info
> + *
> + * Capture a cross timestamp between the system time and the device PTP hardware
> + * clock.
> + *
> + * Return: cross timestamp value on success, -errno on failure.
> + */
> +static int idpf_ptp_get_crosststamp(struct ptp_clock_info *info,
> +				    struct system_device_crosststamp *cts)
> +{
> +	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
> +
> +	return get_device_system_crosststamp(idpf_ptp_get_sync_device_time,
> +					     adapter, NULL, cts);
> +}
> +#endif /* CONFIG_ARM_ARCH_TIMER || (CONFIG_X86 && CONFIG_PCIE_PTM) */
> +
> +/**
> + * idpf_ptp_gettimex64 - Get the time of the clock
> + * @info: the driver's PTP info structure
> + * @ts: timespec64 structure to hold the current time value
> + * @sts: Optional parameter for holding a pair of system timestamps from
> + *	 the system clock. Will be ignored if NULL is given.
> + *
> + * Return: the device clock value in ns, after converting it into a timespec
> + * struct on success, -errno otherwise.
> + */
> +static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
> +			       struct timespec64 *ts,
> +			       struct ptp_system_timestamp *sts)
> +{
> +	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
> +	u64 time_ns;
> +	int err;
> +
> +	err = idpf_ptp_read_src_clk_reg(adapter, &time_ns, sts);
> +	if (err)
> +		return -EACCES;
> +
> +	*ts = ns_to_timespec64(time_ns);
> +
> +	return 0;
> +}
> +
> +/**
> + * idpf_ptp_set_caps - Set PTP capabilities
> + * @adapter: Driver specific private structure
> + *
> + * This function sets the PTP functions.
> + */
> +static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
> +{
> +	struct ptp_clock_info *info = &adapter->ptp->info;
> +
> +	snprintf(info->name, sizeof(info->name), "%s-%s-clk",
> +		 KBUILD_MODNAME, pci_name(adapter->pdev));
> +
> +	info->owner = THIS_MODULE;
> +	info->gettimex64 = idpf_ptp_gettimex64;
> +
> +#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER)
> +	info->getcrosststamp = idpf_ptp_get_crosststamp;
> +#elif IS_ENABLED(CONFIG_X86) && IS_ENABLED(CONFIG_PCIE_PTM)
> +	if (pcie_ptm_enabled(adapter->pdev) &&
> +	    boot_cpu_has(X86_FEATURE_ART) &&
> +	    boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ))
> +		info->getcrosststamp = idpf_ptp_get_crosststamp;
> +#endif /* CONFIG_ARM_ARCH_TIMER */

Looks like this part can be rewritten without macroses. IS_ENABLED() can
be used in normal if() clause, pcie_ptm_enabled() is available anyway, 
feature checks are also OK. The code will be cleaner, I believe.

> +}
> +
>   /**
>    * idpf_ptp_create_clock - Create PTP clock device for userspace
>    * @adapter: Driver specific private structure
> @@ -16,6 +268,8 @@ static int idpf_ptp_create_clock(const struct idpf_adapter *adapter)
>   {
>   	struct ptp_clock *clock;
>   
> +	idpf_ptp_set_caps(adapter);
> +
>   	/* Attempt to register the clock before enabling the hardware. */
>   	clock = ptp_clock_register(&adapter->ptp->info,
>   				   &adapter->pdev->dev);
> @@ -55,6 +309,15 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
>   	/* add a back pointer to adapter */
>   	adapter->ptp->adapter = adapter;
>   
> +	if (adapter->dev_ops.reg_ops.ptp_reg_init)
> +		adapter->dev_ops.reg_ops.ptp_reg_init(adapter);
> +
> +	err = idpf_ptp_get_caps(adapter);
> +	if (err) {
> +		pci_err(adapter->pdev, "Failed to get PTP caps err %d\n", err);
> +		goto free_ptp;
> +	}
> +
>   	err = idpf_ptp_create_clock(adapter);
>   	if (err)
>   		goto free_ptp;
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
> index cb19988ca60f..9b7439f30009 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
> +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
> @@ -6,21 +6,97 @@
>   
>   #include <linux/ptp_clock_kernel.h>
>   
> +/**
> + * struct idpf_ptp_cmd - PTP command masks
> + * @exec_cmd_mask: mask to trigger command execution
> + * @shtime_enable_mask: mask to enable shadow time
> + */
> +struct idpf_ptp_cmd {
> +	u32 exec_cmd_mask;
> +	u32 shtime_enable_mask;
> +};
> +
> +/* struct idpf_ptp_dev_clk_regs - PTP device registers
> + * @dev_clk_ns_l: low part of the device clock register
> + * @dev_clk_ns_h: high part of the device clock register
> + * @phy_clk_ns_l: low part of the PHY clock register
> + * @phy_clk_ns_h: high part of the PHY clock register
> + * @sys_time_ns_l: low part of the system time register
> + * @sys_time_ns_h: high part of the system time register
> + * @cmd: PTP command register
> + * @phy_cmd: PHY command register
> + * @cmd_sync: PTP command synchronization register
> + */
> +struct idpf_ptp_dev_clk_regs {
> +	/* Main clock */
> +	void __iomem *dev_clk_ns_l;
> +	void __iomem *dev_clk_ns_h;
> +
> +	/* PHY timer */
> +	void __iomem *phy_clk_ns_l;
> +	void __iomem *phy_clk_ns_h;
> +
> +	/* System time */
> +	void __iomem *sys_time_ns_l;
> +	void __iomem *sys_time_ns_h;
> +
> +	/* Command */
> +	void __iomem *cmd;
> +	void __iomem *phy_cmd;
> +	void __iomem *cmd_sync;
> +};
> +
> +/**
> + * enum idpf_ptp_access - the type of access to PTP operations
> + * @IDPF_PTP_NONE: no access
> + * @IDPF_PTP_DIRECT: direct access through BAR registers
> + * @IDPF_PTP_MAILBOX: access through mailbox messages
> + */
> +enum idpf_ptp_access {
> +	IDPF_PTP_NONE = 0,
> +	IDPF_PTP_DIRECT,
> +	IDPF_PTP_MAILBOX,
> +};
> +
>   /**
>    * struct idpf_ptp - PTP parameters
>    * @info: structure defining PTP hardware capabilities
>    * @clock: pointer to registered PTP clock device
>    * @adapter: back pointer to the adapter
> + * @cmd: HW specific command masks
> + * @dev_clk_regs: the set of registers to access the device clock
> + * @caps: PTP capabilities negotiated with the Control Plane
> + * @get_dev_clk_time_access: access type for getting the device clock time
> + * @get_cross_tstamp_access: access type for the cross timestamping
>    */
>   struct idpf_ptp {
>   	struct ptp_clock_info info;
>   	struct ptp_clock *clock;
>   	struct idpf_adapter *adapter;
> +	struct idpf_ptp_cmd cmd;
> +	struct idpf_ptp_dev_clk_regs dev_clk_regs;
> +	u32 caps;
> +	enum idpf_ptp_access get_dev_clk_time_access:16;
> +	enum idpf_ptp_access get_cross_tstamp_access:16;
>   };
>   
> +/**
> + * idpf_ptp_info_to_adapter - get driver adapter struct from ptp_clock_info
> + * @info: pointer to ptp_clock_info struct
> + */
> +static inline struct idpf_adapter *
> +idpf_ptp_info_to_adapter(const struct ptp_clock_info *info)
> +{
> +	const struct idpf_ptp *ptp = container_of_const(info, struct idpf_ptp,
> +							info);
> +	return ptp->adapter;
> +}
> +
>   #if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
>   int idpf_ptp_init(struct idpf_adapter *adapter);
>   void idpf_ptp_release(struct idpf_adapter *adapter);
> +int idpf_ptp_get_caps(struct idpf_adapter *adapter);
> +void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
>   #else /* CONFIG_PTP_1588_CLOCK */
>   static inline int idpf_ptp_init(struct idpf_adapter *adpater)
>   {
> @@ -28,5 +104,14 @@ static inline int idpf_ptp_init(struct idpf_adapter *adpater)
>   }
>   
>   static inline void idpf_ptp_release(struct idpf_adapter *adpater) { }
> +
> +static inline int idpf_ptp_get_caps(struct idpf_adapter *adapter)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void
> +idpf_ptp_get_features_access(const struct idpf_adapter *adapter) { }
> +
>   #endif /* CONFIG_PTP_1588_CLOCK */
>   #endif /* _IDPF_PTP_H */
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
> new file mode 100644
> index 000000000000..123bc0008d43
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
> @@ -0,0 +1,95 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2024 Intel Corporation */
> +
> +#include "idpf.h"
> +#include "idpf_ptp.h"
> +#include "idpf_virtchnl.h"
> +
> +/**
> + * idpf_ptp_get_caps - Send virtchnl get ptp capabilities message
> + * @adapter: Driver specific private structure
> + *
> + * Send virtchnl get PTP capabilities message.
> + *
> + * Return: 0 on success, -errno on failure.
> + */
> +int idpf_ptp_get_caps(struct idpf_adapter *adapter)
> +{
> +	struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg __free(kfree) = NULL;
> +	struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {
> +		.caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME |
> +				    VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB |
> +				    VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
> +				    VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB)
> +	};
> +	struct idpf_vc_xn_params xn_params = {
> +		.vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
> +		.send_buf.iov_base = &send_ptp_caps_msg,
> +		.send_buf.iov_len = sizeof(send_ptp_caps_msg),
> +		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
> +	};
> +	struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
> +	struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
> +	struct idpf_ptp *ptp = adapter->ptp;
> +	enum idpf_ptp_access access_type;
> +	u32 temp_offset;
> +	int reply_sz;
> +
> +	recv_ptp_caps_msg = kzalloc(sizeof(struct virtchnl2_ptp_get_caps),
> +				    GFP_KERNEL);
> +	if (!recv_ptp_caps_msg)
> +		return -ENOMEM;
> +
> +	xn_params.recv_buf.iov_base = recv_ptp_caps_msg;
> +	xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg);
> +
> +	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
> +	if (reply_sz < 0)
> +		return reply_sz;
> +	else if (reply_sz != sizeof(*recv_ptp_caps_msg))
> +		return -EIO;
> +
> +	ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
> +
> +	/* Determine the access type for the PTP features */
> +	idpf_ptp_get_features_access(adapter);
> +
> +	access_type = ptp->get_dev_clk_time_access;
> +	if (access_type != IDPF_PTP_DIRECT)
> +		goto cross_tstamp;
> +
> +	clock_offsets = recv_ptp_caps_msg->clk_offsets;
> +
> +	temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l);
> +	ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter,
> +							   temp_offset);
> +	temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h);
> +	ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter,
> +							   temp_offset);
> +	temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l);
> +	ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter,
> +							   temp_offset);
> +	temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h);
> +	ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter,
> +							   temp_offset);
> +	temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
> +	ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
> +
> +cross_tstamp:
> +	access_type = ptp->get_cross_tstamp_access;
> +	if (access_type != IDPF_PTP_DIRECT)
> +		return 0;
> +
> +	cross_tstamp_offsets = recv_ptp_caps_msg->cross_time_offsets;
> +
> +	temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_l);
> +	ptp->dev_clk_regs.sys_time_ns_l = idpf_get_reg_addr(adapter,
> +							    temp_offset);
> +	temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_h);
> +	ptp->dev_clk_regs.sys_time_ns_h = idpf_get_reg_addr(adapter,
> +							    temp_offset);
> +	temp_offset = le32_to_cpu(cross_tstamp_offsets.cmd_sync_trigger);
> +	ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
> +
> +	return 0;
> +}