1d734223bSJacob Keller // SPDX-License-Identifier: GPL-2.0
2d734223bSJacob Keller /* Copyright(c) 2024 Intel Corporation. */
3d734223bSJacob Keller
4d734223bSJacob Keller #include "iavf.h"
5d734223bSJacob Keller #include "iavf_ptp.h"
6d734223bSJacob Keller
752e3beacSJacob Keller #define iavf_clock_to_adapter(info) \
852e3beacSJacob Keller container_of_const(info, struct iavf_adapter, ptp.info)
952e3beacSJacob Keller
10d734223bSJacob Keller /**
1151534239SJacob Keller * iavf_ptp_disable_rx_tstamp - Disable timestamping in Rx rings
1251534239SJacob Keller * @adapter: private adapter structure
1351534239SJacob Keller *
1451534239SJacob Keller * Disable timestamp reporting for all Rx rings.
1551534239SJacob Keller */
iavf_ptp_disable_rx_tstamp(struct iavf_adapter * adapter)1651534239SJacob Keller static void iavf_ptp_disable_rx_tstamp(struct iavf_adapter *adapter)
1751534239SJacob Keller {
1851534239SJacob Keller for (u32 i = 0; i < adapter->num_active_queues; i++)
1951534239SJacob Keller adapter->rx_rings[i].flags &= ~IAVF_TXRX_FLAGS_HW_TSTAMP;
2051534239SJacob Keller }
2151534239SJacob Keller
2251534239SJacob Keller /**
2351534239SJacob Keller * iavf_ptp_enable_rx_tstamp - Enable timestamping in Rx rings
2451534239SJacob Keller * @adapter: private adapter structure
2551534239SJacob Keller *
2651534239SJacob Keller * Enable timestamp reporting for all Rx rings.
2751534239SJacob Keller */
iavf_ptp_enable_rx_tstamp(struct iavf_adapter * adapter)2851534239SJacob Keller static void iavf_ptp_enable_rx_tstamp(struct iavf_adapter *adapter)
2951534239SJacob Keller {
3051534239SJacob Keller for (u32 i = 0; i < adapter->num_active_queues; i++)
3151534239SJacob Keller adapter->rx_rings[i].flags |= IAVF_TXRX_FLAGS_HW_TSTAMP;
3251534239SJacob Keller }
3351534239SJacob Keller
3451534239SJacob Keller /**
3551534239SJacob Keller * iavf_ptp_set_timestamp_mode - Set device timestamping mode
3651534239SJacob Keller * @adapter: private adapter structure
3751534239SJacob Keller * @config: pointer to kernel_hwtstamp_config
3851534239SJacob Keller *
3951534239SJacob Keller * Set the timestamping mode requested from the userspace.
4051534239SJacob Keller *
4151534239SJacob Keller * Note: this function always translates Rx timestamp requests for any packet
4251534239SJacob Keller * category into HWTSTAMP_FILTER_ALL.
4351534239SJacob Keller *
4451534239SJacob Keller * Return: 0 on success, negative error code otherwise.
4551534239SJacob Keller */
iavf_ptp_set_timestamp_mode(struct iavf_adapter * adapter,struct kernel_hwtstamp_config * config)4651534239SJacob Keller static int iavf_ptp_set_timestamp_mode(struct iavf_adapter *adapter,
4751534239SJacob Keller struct kernel_hwtstamp_config *config)
4851534239SJacob Keller {
4951534239SJacob Keller /* Reserved for future extensions. */
5051534239SJacob Keller if (config->flags)
5151534239SJacob Keller return -EINVAL;
5251534239SJacob Keller
5351534239SJacob Keller switch (config->tx_type) {
5451534239SJacob Keller case HWTSTAMP_TX_OFF:
5551534239SJacob Keller break;
5651534239SJacob Keller case HWTSTAMP_TX_ON:
5751534239SJacob Keller return -EOPNOTSUPP;
5851534239SJacob Keller default:
5951534239SJacob Keller return -ERANGE;
6051534239SJacob Keller }
6151534239SJacob Keller
6251534239SJacob Keller if (config->rx_filter == HWTSTAMP_FILTER_NONE) {
6351534239SJacob Keller iavf_ptp_disable_rx_tstamp(adapter);
6451534239SJacob Keller return 0;
6551534239SJacob Keller } else if (config->rx_filter > HWTSTAMP_FILTER_NTP_ALL) {
6651534239SJacob Keller return -ERANGE;
6751534239SJacob Keller } else if (!(iavf_ptp_cap_supported(adapter,
6851534239SJacob Keller VIRTCHNL_1588_PTP_CAP_RX_TSTAMP))) {
6951534239SJacob Keller return -EOPNOTSUPP;
7051534239SJacob Keller }
7151534239SJacob Keller
7251534239SJacob Keller config->rx_filter = HWTSTAMP_FILTER_ALL;
7351534239SJacob Keller iavf_ptp_enable_rx_tstamp(adapter);
7451534239SJacob Keller
7551534239SJacob Keller return 0;
7651534239SJacob Keller }
7751534239SJacob Keller
7851534239SJacob Keller /**
7951534239SJacob Keller * iavf_ptp_set_ts_config - Set timestamping configuration
8051534239SJacob Keller * @adapter: private adapter structure
8151534239SJacob Keller * @config: pointer to kernel_hwtstamp_config structure
8251534239SJacob Keller * @extack: pointer to netlink_ext_ack structure
8351534239SJacob Keller *
8451534239SJacob Keller * Program the requested timestamping configuration to the device.
8551534239SJacob Keller *
8651534239SJacob Keller * Return: 0 on success, negative error code otherwise.
8751534239SJacob Keller */
iavf_ptp_set_ts_config(struct iavf_adapter * adapter,struct kernel_hwtstamp_config * config,struct netlink_ext_ack * extack)8851534239SJacob Keller int iavf_ptp_set_ts_config(struct iavf_adapter *adapter,
8951534239SJacob Keller struct kernel_hwtstamp_config *config,
9051534239SJacob Keller struct netlink_ext_ack *extack)
9151534239SJacob Keller {
9251534239SJacob Keller int err;
9351534239SJacob Keller
9451534239SJacob Keller err = iavf_ptp_set_timestamp_mode(adapter, config);
9551534239SJacob Keller if (err)
9651534239SJacob Keller return err;
9751534239SJacob Keller
9851534239SJacob Keller /* Save successful settings for future reference */
9951534239SJacob Keller adapter->ptp.hwtstamp_config = *config;
10051534239SJacob Keller
10151534239SJacob Keller return 0;
10251534239SJacob Keller }
10351534239SJacob Keller
10451534239SJacob Keller /**
105d734223bSJacob Keller * iavf_ptp_cap_supported - Check if a PTP capability is supported
106d734223bSJacob Keller * @adapter: private adapter structure
107d734223bSJacob Keller * @cap: the capability bitmask to check
108d734223bSJacob Keller *
109d734223bSJacob Keller * Return: true if every capability set in cap is also set in the enabled
110d734223bSJacob Keller * capabilities reported by the PF, false otherwise.
111d734223bSJacob Keller */
iavf_ptp_cap_supported(const struct iavf_adapter * adapter,u32 cap)112d734223bSJacob Keller bool iavf_ptp_cap_supported(const struct iavf_adapter *adapter, u32 cap)
113d734223bSJacob Keller {
114d734223bSJacob Keller if (!IAVF_PTP_ALLOWED(adapter))
115d734223bSJacob Keller return false;
116d734223bSJacob Keller
117d734223bSJacob Keller /* Only return true if every bit in cap is set in hw_caps.caps */
118d734223bSJacob Keller return (adapter->ptp.hw_caps.caps & cap) == cap;
119d734223bSJacob Keller }
120d734223bSJacob Keller
121d734223bSJacob Keller /**
12252e3beacSJacob Keller * iavf_allocate_ptp_cmd - Allocate a PTP command message structure
12352e3beacSJacob Keller * @v_opcode: the virtchnl opcode
12452e3beacSJacob Keller * @msglen: length in bytes of the associated virtchnl structure
12552e3beacSJacob Keller *
12652e3beacSJacob Keller * Allocates a PTP command message and pre-fills it with the provided message
12752e3beacSJacob Keller * length and opcode.
12852e3beacSJacob Keller *
12952e3beacSJacob Keller * Return: allocated PTP command.
13052e3beacSJacob Keller */
iavf_allocate_ptp_cmd(enum virtchnl_ops v_opcode,u16 msglen)13152e3beacSJacob Keller static struct iavf_ptp_aq_cmd *iavf_allocate_ptp_cmd(enum virtchnl_ops v_opcode,
13252e3beacSJacob Keller u16 msglen)
13352e3beacSJacob Keller {
13452e3beacSJacob Keller struct iavf_ptp_aq_cmd *cmd;
13552e3beacSJacob Keller
13652e3beacSJacob Keller cmd = kzalloc(struct_size(cmd, msg, msglen), GFP_KERNEL);
13752e3beacSJacob Keller if (!cmd)
13852e3beacSJacob Keller return NULL;
13952e3beacSJacob Keller
14052e3beacSJacob Keller cmd->v_opcode = v_opcode;
14152e3beacSJacob Keller cmd->msglen = msglen;
14252e3beacSJacob Keller
14352e3beacSJacob Keller return cmd;
14452e3beacSJacob Keller }
14552e3beacSJacob Keller
14652e3beacSJacob Keller /**
14752e3beacSJacob Keller * iavf_queue_ptp_cmd - Queue PTP command for sending over virtchnl
14852e3beacSJacob Keller * @adapter: private adapter structure
14952e3beacSJacob Keller * @cmd: the command structure to send
15052e3beacSJacob Keller *
15152e3beacSJacob Keller * Queue the given command structure into the PTP virtchnl command queue tos
15252e3beacSJacob Keller * end to the PF.
15352e3beacSJacob Keller */
iavf_queue_ptp_cmd(struct iavf_adapter * adapter,struct iavf_ptp_aq_cmd * cmd)15452e3beacSJacob Keller static void iavf_queue_ptp_cmd(struct iavf_adapter *adapter,
15552e3beacSJacob Keller struct iavf_ptp_aq_cmd *cmd)
15652e3beacSJacob Keller {
15752e3beacSJacob Keller mutex_lock(&adapter->ptp.aq_cmd_lock);
15852e3beacSJacob Keller list_add_tail(&cmd->list, &adapter->ptp.aq_cmds);
15952e3beacSJacob Keller mutex_unlock(&adapter->ptp.aq_cmd_lock);
16052e3beacSJacob Keller
16152e3beacSJacob Keller adapter->aq_required |= IAVF_FLAG_AQ_SEND_PTP_CMD;
16252e3beacSJacob Keller mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
16352e3beacSJacob Keller }
16452e3beacSJacob Keller
16552e3beacSJacob Keller /**
16652e3beacSJacob Keller * iavf_send_phc_read - Send request to read PHC time
16752e3beacSJacob Keller * @adapter: private adapter structure
16852e3beacSJacob Keller *
16952e3beacSJacob Keller * Send a request to obtain the PTP hardware clock time. This allocates the
17052e3beacSJacob Keller * VIRTCHNL_OP_1588_PTP_GET_TIME message and queues it up to send to
17152e3beacSJacob Keller * indirectly read the PHC time.
17252e3beacSJacob Keller *
17352e3beacSJacob Keller * This function does not wait for the reply from the PF.
17452e3beacSJacob Keller *
17552e3beacSJacob Keller * Return: 0 if success, error code otherwise.
17652e3beacSJacob Keller */
iavf_send_phc_read(struct iavf_adapter * adapter)17752e3beacSJacob Keller static int iavf_send_phc_read(struct iavf_adapter *adapter)
17852e3beacSJacob Keller {
17952e3beacSJacob Keller struct iavf_ptp_aq_cmd *cmd;
18052e3beacSJacob Keller
18152e3beacSJacob Keller if (!adapter->ptp.clock)
18252e3beacSJacob Keller return -EOPNOTSUPP;
18352e3beacSJacob Keller
18452e3beacSJacob Keller cmd = iavf_allocate_ptp_cmd(VIRTCHNL_OP_1588_PTP_GET_TIME,
18552e3beacSJacob Keller sizeof(struct virtchnl_phc_time));
18652e3beacSJacob Keller if (!cmd)
18752e3beacSJacob Keller return -ENOMEM;
18852e3beacSJacob Keller
18952e3beacSJacob Keller iavf_queue_ptp_cmd(adapter, cmd);
19052e3beacSJacob Keller
19152e3beacSJacob Keller return 0;
19252e3beacSJacob Keller }
19352e3beacSJacob Keller
19452e3beacSJacob Keller /**
19552e3beacSJacob Keller * iavf_read_phc_indirect - Indirectly read the PHC time via virtchnl
19652e3beacSJacob Keller * @adapter: private adapter structure
19752e3beacSJacob Keller * @ts: storage for the timestamp value
19852e3beacSJacob Keller * @sts: system timestamp values before and after the read
19952e3beacSJacob Keller *
20052e3beacSJacob Keller * Used when the device does not have direct register access to the PHC time.
20152e3beacSJacob Keller * Indirectly reads the time via the VIRTCHNL_OP_1588_PTP_GET_TIME, and waits
20252e3beacSJacob Keller * for the reply from the PF.
20352e3beacSJacob Keller *
20452e3beacSJacob Keller * Based on some simple measurements using ftrace and phc2sys, this clock
20552e3beacSJacob Keller * access method has about a ~110 usec latency even when the system is not
20652e3beacSJacob Keller * under load. In order to achieve acceptable results when using phc2sys with
20752e3beacSJacob Keller * the indirect clock access method, it is recommended to use more
20852e3beacSJacob Keller * conservative proportional and integration constants with the P/I servo.
20952e3beacSJacob Keller *
21052e3beacSJacob Keller * Return: 0 if success, error code otherwise.
21152e3beacSJacob Keller */
iavf_read_phc_indirect(struct iavf_adapter * adapter,struct timespec64 * ts,struct ptp_system_timestamp * sts)21252e3beacSJacob Keller static int iavf_read_phc_indirect(struct iavf_adapter *adapter,
21352e3beacSJacob Keller struct timespec64 *ts,
21452e3beacSJacob Keller struct ptp_system_timestamp *sts)
21552e3beacSJacob Keller {
21652e3beacSJacob Keller long ret;
21752e3beacSJacob Keller int err;
21852e3beacSJacob Keller
21952e3beacSJacob Keller adapter->ptp.phc_time_ready = false;
22052e3beacSJacob Keller
22152e3beacSJacob Keller ptp_read_system_prets(sts);
22252e3beacSJacob Keller
22352e3beacSJacob Keller err = iavf_send_phc_read(adapter);
22452e3beacSJacob Keller if (err)
22552e3beacSJacob Keller return err;
22652e3beacSJacob Keller
22752e3beacSJacob Keller ret = wait_event_interruptible_timeout(adapter->ptp.phc_time_waitqueue,
22852e3beacSJacob Keller adapter->ptp.phc_time_ready,
22952e3beacSJacob Keller HZ);
23052e3beacSJacob Keller
23152e3beacSJacob Keller ptp_read_system_postts(sts);
23252e3beacSJacob Keller
23352e3beacSJacob Keller if (ret < 0)
23452e3beacSJacob Keller return ret;
23552e3beacSJacob Keller else if (!ret)
23652e3beacSJacob Keller return -EBUSY;
23752e3beacSJacob Keller
23852e3beacSJacob Keller *ts = ns_to_timespec64(adapter->ptp.cached_phc_time);
23952e3beacSJacob Keller
24052e3beacSJacob Keller return 0;
24152e3beacSJacob Keller }
24252e3beacSJacob Keller
iavf_ptp_gettimex64(struct ptp_clock_info * info,struct timespec64 * ts,struct ptp_system_timestamp * sts)24352e3beacSJacob Keller static int iavf_ptp_gettimex64(struct ptp_clock_info *info,
24452e3beacSJacob Keller struct timespec64 *ts,
24552e3beacSJacob Keller struct ptp_system_timestamp *sts)
24652e3beacSJacob Keller {
24752e3beacSJacob Keller struct iavf_adapter *adapter = iavf_clock_to_adapter(info);
24852e3beacSJacob Keller
24952e3beacSJacob Keller if (!adapter->ptp.clock)
25052e3beacSJacob Keller return -EOPNOTSUPP;
25152e3beacSJacob Keller
25252e3beacSJacob Keller return iavf_read_phc_indirect(adapter, ts, sts);
25352e3beacSJacob Keller }
25452e3beacSJacob Keller
25552e3beacSJacob Keller /**
2567c01dbfcSJacob Keller * iavf_ptp_cache_phc_time - Cache PHC time for performing timestamp extension
2577c01dbfcSJacob Keller * @adapter: private adapter structure
2587c01dbfcSJacob Keller *
2597c01dbfcSJacob Keller * Periodically cache the PHC time in order to allow for timestamp extension.
2607c01dbfcSJacob Keller * This is required because the Tx and Rx timestamps only contain 32bits of
2617c01dbfcSJacob Keller * nanoseconds. Timestamp extension allows calculating the corrected 64bit
2627c01dbfcSJacob Keller * timestamp. This algorithm relies on the cached time being within ~1 second
2637c01dbfcSJacob Keller * of the timestamp.
2647c01dbfcSJacob Keller */
iavf_ptp_cache_phc_time(struct iavf_adapter * adapter)2657c01dbfcSJacob Keller static void iavf_ptp_cache_phc_time(struct iavf_adapter *adapter)
2667c01dbfcSJacob Keller {
2677c01dbfcSJacob Keller if (!time_is_before_jiffies(adapter->ptp.cached_phc_updated + HZ))
2687c01dbfcSJacob Keller return;
2697c01dbfcSJacob Keller
2707c01dbfcSJacob Keller /* The response from virtchnl will store the time into
2717c01dbfcSJacob Keller * cached_phc_time.
2727c01dbfcSJacob Keller */
2737c01dbfcSJacob Keller iavf_send_phc_read(adapter);
2747c01dbfcSJacob Keller }
2757c01dbfcSJacob Keller
2767c01dbfcSJacob Keller /**
2777c01dbfcSJacob Keller * iavf_ptp_do_aux_work - Perform periodic work required for PTP support
2787c01dbfcSJacob Keller * @info: PTP clock info structure
2797c01dbfcSJacob Keller *
2807c01dbfcSJacob Keller * Handler to take care of periodic work required for PTP operation. This
2817c01dbfcSJacob Keller * includes the following tasks:
2827c01dbfcSJacob Keller *
2837c01dbfcSJacob Keller * 1) updating cached_phc_time
2847c01dbfcSJacob Keller *
2857c01dbfcSJacob Keller * cached_phc_time is used by the Tx and Rx timestamp flows in order to
2867c01dbfcSJacob Keller * perform timestamp extension, by carefully comparing the timestamp
2877c01dbfcSJacob Keller * 32bit nanosecond timestamps and determining the corrected 64bit
2887c01dbfcSJacob Keller * timestamp value to report to userspace. This algorithm only works if
2897c01dbfcSJacob Keller * the cached_phc_time is within ~1 second of the Tx or Rx timestamp
2907c01dbfcSJacob Keller * event. This task periodically reads the PHC time and stores it, to
2917c01dbfcSJacob Keller * ensure that timestamp extension operates correctly.
2927c01dbfcSJacob Keller *
2937c01dbfcSJacob Keller * Returns: time in jiffies until the periodic task should be re-scheduled.
2947c01dbfcSJacob Keller */
iavf_ptp_do_aux_work(struct ptp_clock_info * info)2957c01dbfcSJacob Keller static long iavf_ptp_do_aux_work(struct ptp_clock_info *info)
2967c01dbfcSJacob Keller {
2977c01dbfcSJacob Keller struct iavf_adapter *adapter = iavf_clock_to_adapter(info);
2987c01dbfcSJacob Keller
2997c01dbfcSJacob Keller iavf_ptp_cache_phc_time(adapter);
3007c01dbfcSJacob Keller
3017c01dbfcSJacob Keller /* Check work about twice a second */
3027c01dbfcSJacob Keller return msecs_to_jiffies(500);
3037c01dbfcSJacob Keller }
3047c01dbfcSJacob Keller
3057c01dbfcSJacob Keller /**
306d734223bSJacob Keller * iavf_ptp_register_clock - Register a new PTP for userspace
307d734223bSJacob Keller * @adapter: private adapter structure
308d734223bSJacob Keller *
309d734223bSJacob Keller * Allocate and register a new PTP clock device if necessary.
310d734223bSJacob Keller *
311d734223bSJacob Keller * Return: 0 if success, error otherwise.
312d734223bSJacob Keller */
iavf_ptp_register_clock(struct iavf_adapter * adapter)313d734223bSJacob Keller static int iavf_ptp_register_clock(struct iavf_adapter *adapter)
314d734223bSJacob Keller {
315d734223bSJacob Keller struct ptp_clock_info *ptp_info = &adapter->ptp.info;
316d734223bSJacob Keller struct device *dev = &adapter->pdev->dev;
317d734223bSJacob Keller struct ptp_clock *clock;
318d734223bSJacob Keller
319d734223bSJacob Keller snprintf(ptp_info->name, sizeof(ptp_info->name), "%s-%s-clk",
320d734223bSJacob Keller KBUILD_MODNAME, dev_name(dev));
321d734223bSJacob Keller ptp_info->owner = THIS_MODULE;
32252e3beacSJacob Keller ptp_info->gettimex64 = iavf_ptp_gettimex64;
3237c01dbfcSJacob Keller ptp_info->do_aux_work = iavf_ptp_do_aux_work;
324d734223bSJacob Keller
325d734223bSJacob Keller clock = ptp_clock_register(ptp_info, dev);
326d734223bSJacob Keller if (IS_ERR(clock))
327d734223bSJacob Keller return PTR_ERR(clock);
328d734223bSJacob Keller
329d734223bSJacob Keller adapter->ptp.clock = clock;
330d734223bSJacob Keller
331d734223bSJacob Keller dev_dbg(&adapter->pdev->dev, "PTP clock %s registered\n",
332d734223bSJacob Keller adapter->ptp.info.name);
333d734223bSJacob Keller
334d734223bSJacob Keller return 0;
335d734223bSJacob Keller }
336d734223bSJacob Keller
337d734223bSJacob Keller /**
338d734223bSJacob Keller * iavf_ptp_init - Initialize PTP support if capability was negotiated
339d734223bSJacob Keller * @adapter: private adapter structure
340d734223bSJacob Keller *
341d734223bSJacob Keller * Initialize PTP functionality, based on the capabilities that the PF has
342d734223bSJacob Keller * enabled for this VF.
343d734223bSJacob Keller */
iavf_ptp_init(struct iavf_adapter * adapter)344d734223bSJacob Keller void iavf_ptp_init(struct iavf_adapter *adapter)
345d734223bSJacob Keller {
346d734223bSJacob Keller int err;
347d734223bSJacob Keller
348d734223bSJacob Keller if (!iavf_ptp_cap_supported(adapter, VIRTCHNL_1588_PTP_CAP_READ_PHC)) {
349d734223bSJacob Keller pci_notice(adapter->pdev,
350d734223bSJacob Keller "Device does not have PTP clock support\n");
351d734223bSJacob Keller return;
352d734223bSJacob Keller }
353d734223bSJacob Keller
354d734223bSJacob Keller err = iavf_ptp_register_clock(adapter);
355d734223bSJacob Keller if (err) {
356d734223bSJacob Keller pci_err(adapter->pdev,
357d734223bSJacob Keller "Failed to register PTP clock device (%p)\n",
358d734223bSJacob Keller ERR_PTR(err));
359d734223bSJacob Keller return;
360d734223bSJacob Keller }
361d734223bSJacob Keller
362d734223bSJacob Keller for (int i = 0; i < adapter->num_active_queues; i++) {
363d734223bSJacob Keller struct iavf_ring *rx_ring = &adapter->rx_rings[i];
364d734223bSJacob Keller
365d734223bSJacob Keller rx_ring->ptp = &adapter->ptp;
366d734223bSJacob Keller }
3677c01dbfcSJacob Keller
3687c01dbfcSJacob Keller ptp_schedule_worker(adapter->ptp.clock, 0);
369d734223bSJacob Keller }
370d734223bSJacob Keller
371d734223bSJacob Keller /**
372d734223bSJacob Keller * iavf_ptp_release - Disable PTP support
373d734223bSJacob Keller * @adapter: private adapter structure
374d734223bSJacob Keller *
375d734223bSJacob Keller * Release all PTP resources that were previously initialized.
376d734223bSJacob Keller */
iavf_ptp_release(struct iavf_adapter * adapter)377d734223bSJacob Keller void iavf_ptp_release(struct iavf_adapter *adapter)
378d734223bSJacob Keller {
37952e3beacSJacob Keller struct iavf_ptp_aq_cmd *cmd, *tmp;
38052e3beacSJacob Keller
381d734223bSJacob Keller if (!adapter->ptp.clock)
382d734223bSJacob Keller return;
383d734223bSJacob Keller
384d734223bSJacob Keller pci_dbg(adapter->pdev, "removing PTP clock %s\n",
385d734223bSJacob Keller adapter->ptp.info.name);
386d734223bSJacob Keller ptp_clock_unregister(adapter->ptp.clock);
387d734223bSJacob Keller adapter->ptp.clock = NULL;
38852e3beacSJacob Keller
38952e3beacSJacob Keller /* Cancel any remaining uncompleted PTP clock commands */
39052e3beacSJacob Keller mutex_lock(&adapter->ptp.aq_cmd_lock);
39152e3beacSJacob Keller list_for_each_entry_safe(cmd, tmp, &adapter->ptp.aq_cmds, list) {
39252e3beacSJacob Keller list_del(&cmd->list);
39352e3beacSJacob Keller kfree(cmd);
39452e3beacSJacob Keller }
39552e3beacSJacob Keller adapter->aq_required &= ~IAVF_FLAG_AQ_SEND_PTP_CMD;
39652e3beacSJacob Keller mutex_unlock(&adapter->ptp.aq_cmd_lock);
397*48ccdcd8SJacob Keller
398*48ccdcd8SJacob Keller adapter->ptp.hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
399*48ccdcd8SJacob Keller iavf_ptp_disable_rx_tstamp(adapter);
400d734223bSJacob Keller }
401d734223bSJacob Keller
402d734223bSJacob Keller /**
403d734223bSJacob Keller * iavf_ptp_process_caps - Handle change in PTP capabilities
404d734223bSJacob Keller * @adapter: private adapter structure
405d734223bSJacob Keller *
406d734223bSJacob Keller * Handle any state changes necessary due to change in PTP capabilities, such
407d734223bSJacob Keller * as after a device reset or change in configuration from the PF.
408d734223bSJacob Keller */
iavf_ptp_process_caps(struct iavf_adapter * adapter)409d734223bSJacob Keller void iavf_ptp_process_caps(struct iavf_adapter *adapter)
410d734223bSJacob Keller {
411d734223bSJacob Keller bool phc = iavf_ptp_cap_supported(adapter, VIRTCHNL_1588_PTP_CAP_READ_PHC);
412d734223bSJacob Keller
413d734223bSJacob Keller /* Check if the device gained or lost necessary access to support the
414d734223bSJacob Keller * PTP hardware clock. If so, driver must respond appropriately by
415d734223bSJacob Keller * creating or destroying the PTP clock device.
416d734223bSJacob Keller */
417d734223bSJacob Keller if (adapter->ptp.clock && !phc)
418d734223bSJacob Keller iavf_ptp_release(adapter);
419d734223bSJacob Keller else if (!adapter->ptp.clock && phc)
420d734223bSJacob Keller iavf_ptp_init(adapter);
42151534239SJacob Keller
42251534239SJacob Keller /* Check if the device lost access to Rx timestamp incoming packets */
42351534239SJacob Keller if (!iavf_ptp_cap_supported(adapter, VIRTCHNL_1588_PTP_CAP_RX_TSTAMP)) {
42451534239SJacob Keller adapter->ptp.hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
42551534239SJacob Keller iavf_ptp_disable_rx_tstamp(adapter);
42651534239SJacob Keller }
427d734223bSJacob Keller }
428*48ccdcd8SJacob Keller
429*48ccdcd8SJacob Keller /**
430*48ccdcd8SJacob Keller * iavf_ptp_extend_32b_timestamp - Convert a 32b nanoseconds timestamp to 64b
431*48ccdcd8SJacob Keller * nanoseconds
432*48ccdcd8SJacob Keller * @cached_phc_time: recently cached copy of PHC time
433*48ccdcd8SJacob Keller * @in_tstamp: Ingress/egress 32b nanoseconds timestamp value
434*48ccdcd8SJacob Keller *
435*48ccdcd8SJacob Keller * Hardware captures timestamps which contain only 32 bits of nominal
436*48ccdcd8SJacob Keller * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
437*48ccdcd8SJacob Keller *
438*48ccdcd8SJacob Keller * Extend the 32bit nanosecond timestamp using the following algorithm and
439*48ccdcd8SJacob Keller * assumptions:
440*48ccdcd8SJacob Keller *
441*48ccdcd8SJacob Keller * 1) have a recently cached copy of the PHC time
442*48ccdcd8SJacob Keller * 2) assume that the in_tstamp was captured 2^31 nanoseconds (~2.1
443*48ccdcd8SJacob Keller * seconds) before or after the PHC time was captured.
444*48ccdcd8SJacob Keller * 3) calculate the delta between the cached time and the timestamp
445*48ccdcd8SJacob Keller * 4) if the delta is smaller than 2^31 nanoseconds, then the timestamp was
446*48ccdcd8SJacob Keller * captured after the PHC time. In this case, the full timestamp is just
447*48ccdcd8SJacob Keller * the cached PHC time plus the delta.
448*48ccdcd8SJacob Keller * 5) otherwise, if the delta is larger than 2^31 nanoseconds, then the
449*48ccdcd8SJacob Keller * timestamp was captured *before* the PHC time, i.e. because the PHC
450*48ccdcd8SJacob Keller * cache was updated after the timestamp was captured by hardware. In this
451*48ccdcd8SJacob Keller * case, the full timestamp is the cached time minus the inverse delta.
452*48ccdcd8SJacob Keller *
453*48ccdcd8SJacob Keller * This algorithm works even if the PHC time was updated after a Tx timestamp
454*48ccdcd8SJacob Keller * was requested, but before the Tx timestamp event was reported from
455*48ccdcd8SJacob Keller * hardware.
456*48ccdcd8SJacob Keller *
457*48ccdcd8SJacob Keller * This calculation primarily relies on keeping the cached PHC time up to
458*48ccdcd8SJacob Keller * date. If the timestamp was captured more than 2^31 nanoseconds after the
459*48ccdcd8SJacob Keller * PHC time, it is possible that the lower 32bits of PHC time have
460*48ccdcd8SJacob Keller * overflowed more than once, and we might generate an incorrect timestamp.
461*48ccdcd8SJacob Keller *
462*48ccdcd8SJacob Keller * This is prevented by (a) periodically updating the cached PHC time once
463*48ccdcd8SJacob Keller * a second, and (b) discarding any Tx timestamp packet if it has waited for
464*48ccdcd8SJacob Keller * a timestamp for more than one second.
465*48ccdcd8SJacob Keller *
466*48ccdcd8SJacob Keller * Return: extended timestamp (to 64b).
467*48ccdcd8SJacob Keller */
iavf_ptp_extend_32b_timestamp(u64 cached_phc_time,u32 in_tstamp)468*48ccdcd8SJacob Keller u64 iavf_ptp_extend_32b_timestamp(u64 cached_phc_time, u32 in_tstamp)
469*48ccdcd8SJacob Keller {
470*48ccdcd8SJacob Keller u32 low = lower_32_bits(cached_phc_time);
471*48ccdcd8SJacob Keller u32 delta = in_tstamp - low;
472*48ccdcd8SJacob Keller u64 ns;
473*48ccdcd8SJacob Keller
474*48ccdcd8SJacob Keller /* Do not assume that the in_tstamp is always more recent than the
475*48ccdcd8SJacob Keller * cached PHC time. If the delta is large, it indicates that the
476*48ccdcd8SJacob Keller * in_tstamp was taken in the past, and should be converted
477*48ccdcd8SJacob Keller * forward.
478*48ccdcd8SJacob Keller */
479*48ccdcd8SJacob Keller if (delta > S32_MAX)
480*48ccdcd8SJacob Keller ns = cached_phc_time - (low - in_tstamp);
481*48ccdcd8SJacob Keller else
482*48ccdcd8SJacob Keller ns = cached_phc_time + delta;
483*48ccdcd8SJacob Keller
484*48ccdcd8SJacob Keller return ns;
485*48ccdcd8SJacob Keller }
486