diff mbox series

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

Message ID 20241113154616.2493297-5-milena.olech@intel.com (mailing list archive)
State Awaiting Upstream
Delegated to: Netdev Maintainers
Headers show
Series initial PTP support | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present fail Series targets non-next tree, but doesn't contain any Fixes tags
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3 this patch: 3
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 5 maintainers not CCed: kuba@kernel.org andrew+netdev@lunn.ch pabeni@redhat.com edumazet@google.com richardcochran@gmail.com
netdev/build_clang success Errors and warnings before: 4 this patch: 4
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 96 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc fail Errors and warnings before: 20 this patch: 21
netdev/source_inline success Was 0 now: 0

Commit Message

Milena Olech Nov. 13, 2024, 3:46 p.m. UTC
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 <aleksander.lobakin@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
 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

Comments

Vadim Fedorenko Nov. 14, 2024, 12:17 p.m. UTC | #1
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 <aleksander.lobakin@intel.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> ---
>   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;
> +}
diff mbox series

Patch

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