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