Contributors: 6
Author Tokens Token Proportion Commits Commit Proportion
Milena Olech 2469 91.72% 7 31.82%
Phani Burra 96 3.57% 1 4.55%
Alan Brady 47 1.75% 5 22.73%
Joshua Hay 40 1.49% 3 13.64%
Pavan Kumar Linga 38 1.41% 4 18.18%
Alexander Lobakin 2 0.07% 2 9.09%
Total 2692 22


// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2024 Intel Corporation */

#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);

	/* Set the device clock time */
	direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
	mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
	ptp->set_dev_clk_time_access = idpf_ptp_get_access(adapter,
							   direct,
							   mailbox);

	/* Adjust the device clock time */
	direct = VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK;
	mailbox = VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB;
	ptp->adj_dev_clk_time_access = idpf_ptp_get_access(adapter,
							   direct,
							   mailbox);

	/* Tx timestamping */
	direct = VIRTCHNL2_CAP_PTP_TX_TSTAMPS;
	mailbox = VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB;
	ptp->tx_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.
 */
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;

	spin_lock(&ptp->read_dev_clk_lock);

	/* Read the system timestamp pre PHC read */
	ptp_read_system_prets(sts);

	idpf_ptp_enable_shtime(adapter);

	/* Read the system timestamp post PHC read */
	ptp_read_system_postts(sts);

	lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
	hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);

	spin_unlock(&ptp->read_dev_clk_lock);

	return ((u64)hi << 32) | lo;
}

/**
 * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
 * @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.
 * @src_clk: Returned main timer value in nanoseconds unit
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_read_src_clk_reg_mailbox(struct idpf_adapter *adapter,
					     struct ptp_system_timestamp *sts,
					     u64 *src_clk)
{
	struct idpf_ptp_dev_timers clk_time;
	int err;

	/* Read the system timestamp pre PHC read */
	ptp_read_system_prets(sts);

	err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
	if (err)
		return err;

	/* Read the system timestamp post PHC read */
	ptp_read_system_postts(sts);

	*src_clk = clk_time.dev_clk_time_ns;

	return 0;
}

/**
 * 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_MAILBOX:
		return idpf_ptp_read_src_clk_reg_mailbox(adapter, sts, src_clk);
	case IDPF_PTP_DIRECT:
		*src_clk = idpf_ptp_read_src_clk_reg_direct(adapter, sts);
		break;
	default:
		return -EOPNOTSUPP;
	}

	return 0;
}

/**
 * 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_update_phctime_rxq_grp - Update the cached PHC time for a given Rx
 *				     queue group.
 * @grp: receive queue group in which Rx timestamp is enabled
 * @split: Indicates whether the queue model is split or single queue
 * @systime: Cached system time
 */
static void
idpf_ptp_update_phctime_rxq_grp(const struct idpf_rxq_group *grp, bool split,
				u64 systime)
{
	struct idpf_rx_queue *rxq;
	u16 i;

	if (!split) {
		for (i = 0; i < grp->singleq.num_rxq; i++) {
			rxq = grp->singleq.rxqs[i];
			if (rxq)
				WRITE_ONCE(rxq->cached_phc_time, systime);
		}
	} else {
		for (i = 0; i < grp->splitq.num_rxq_sets; i++) {
			rxq = &grp->splitq.rxq_sets[i]->rxq;
			if (rxq)
				WRITE_ONCE(rxq->cached_phc_time, systime);
		}
	}
}

/**
 * idpf_ptp_update_cached_phctime - Update the cached PHC time values
 * @adapter: Driver specific private structure
 *
 * This function updates the system time values which are cached in the adapter
 * structure and the Rx queues.
 *
 * This function must be called periodically to ensure that the cached value
 * is never more than 2 seconds old.
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_update_cached_phctime(struct idpf_adapter *adapter)
{
	u64 systime;
	int err;

	err = idpf_ptp_read_src_clk_reg(adapter, &systime, NULL);
	if (err)
		return -EACCES;

	/* Update the cached PHC time stored in the adapter structure.
	 * These values are used to extend Tx timestamp values to 64 bit
	 * expected by the stack.
	 */
	WRITE_ONCE(adapter->ptp->cached_phc_time, systime);
	WRITE_ONCE(adapter->ptp->cached_phc_jiffies, jiffies);

	idpf_for_each_vport(adapter, vport) {
		bool split;

		if (!vport || !vport->rxq_grps)
			continue;

		split = idpf_is_queue_model_split(vport->rxq_model);

		for (u16 i = 0; i < vport->num_rxq_grp; i++) {
			struct idpf_rxq_group *grp = &vport->rxq_grps[i];

			idpf_ptp_update_phctime_rxq_grp(grp, split, systime);
		}
	}

	return 0;
}

/**
 * idpf_ptp_settime64 - Set the time of the clock
 * @info: the driver's PTP info structure
 * @ts: timespec64 structure that holds the new time value
 *
 * Set the device clock to the user input value. The conversion from timespec
 * to ns happens in the write function.
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_settime64(struct ptp_clock_info *info,
			      const struct timespec64 *ts)
{
	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
	enum idpf_ptp_access access;
	int err;
	u64 ns;

	access = adapter->ptp->set_dev_clk_time_access;
	if (access != IDPF_PTP_MAILBOX)
		return -EOPNOTSUPP;

	ns = timespec64_to_ns(ts);

	err = idpf_ptp_set_dev_clk_time(adapter, ns);
	if (err) {
		pci_err(adapter->pdev, "Failed to set the time, err: %pe\n",
			ERR_PTR(err));
		return err;
	}

	err = idpf_ptp_update_cached_phctime(adapter);
	if (err)
		pci_warn(adapter->pdev,
			 "Unable to immediately update cached PHC time\n");

	return 0;
}

/**
 * idpf_ptp_adjtime_nonatomic - Do a non-atomic clock adjustment
 * @info: the driver's PTP info structure
 * @delta: Offset in nanoseconds to adjust the time by
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_adjtime_nonatomic(struct ptp_clock_info *info, s64 delta)
{
	struct timespec64 now, then;
	int err;

	err = idpf_ptp_gettimex64(info, &now, NULL);
	if (err)
		return err;

	then = ns_to_timespec64(delta);
	now = timespec64_add(now, then);

	return idpf_ptp_settime64(info, &now);
}

/**
 * idpf_ptp_adjtime - Adjust the time of the clock by the indicated delta
 * @info: the driver's PTP info structure
 * @delta: Offset in nanoseconds to adjust the time by
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
{
	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
	enum idpf_ptp_access access;
	int err;

	access = adapter->ptp->adj_dev_clk_time_access;
	if (access != IDPF_PTP_MAILBOX)
		return -EOPNOTSUPP;

	/* Hardware only supports atomic adjustments using signed 32-bit
	 * integers. For any adjustment outside this range, perform
	 * a non-atomic get->adjust->set flow.
	 */
	if (delta > S32_MAX || delta < S32_MIN)
		return idpf_ptp_adjtime_nonatomic(info, delta);

	err = idpf_ptp_adj_dev_clk_time(adapter, delta);
	if (err) {
		pci_err(adapter->pdev, "Failed to adjust the clock with delta %lld err: %pe\n",
			delta, ERR_PTR(err));
		return err;
	}

	err = idpf_ptp_update_cached_phctime(adapter);
	if (err)
		pci_warn(adapter->pdev,
			 "Unable to immediately update cached PHC time\n");

	return 0;
}

/**
 * idpf_ptp_adjfine - Adjust clock increment rate
 * @info: the driver's PTP info structure
 * @scaled_ppm: Parts per million with 16-bit fractional field
 *
 * Adjust the frequency of the clock by the indicated scaled ppm from the
 * base frequency.
 *
 * Return: 0 on success, -errno otherwise.
 */
static int idpf_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
{
	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
	enum idpf_ptp_access access;
	u64 incval, diff;
	int err;

	access = adapter->ptp->adj_dev_clk_time_access;
	if (access != IDPF_PTP_MAILBOX)
		return -EOPNOTSUPP;

	incval = adapter->ptp->base_incval;

	diff = adjust_by_scaled_ppm(incval, scaled_ppm);
	err = idpf_ptp_adj_dev_clk_fine(adapter, diff);
	if (err)
		pci_err(adapter->pdev, "Failed to adjust clock increment rate for scaled ppm %ld %pe\n",
			scaled_ppm, ERR_PTR(err));

	return 0;
}

/**
 * idpf_ptp_verify_pin - Verify if pin supports requested pin function
 * @info: the driver's PTP info structure
 * @pin: Pin index
 * @func: Assigned function
 * @chan: Assigned channel
 *
 * Return: EOPNOTSUPP as not supported yet.
 */
static int idpf_ptp_verify_pin(struct ptp_clock_info *info, unsigned int pin,
			       enum ptp_pin_function func, unsigned int chan)
{
	return -EOPNOTSUPP;
}

/**
 * idpf_ptp_gpio_enable - Enable/disable ancillary features of PHC
 * @info: the driver's PTP info structure
 * @rq: The requested feature to change
 * @on: Enable/disable flag
 *
 * Return: EOPNOTSUPP as not supported yet.
 */
static int idpf_ptp_gpio_enable(struct ptp_clock_info *info,
				struct ptp_clock_request *rq, int on)
{
	return -EOPNOTSUPP;
}

/**
 * idpf_ptp_tstamp_extend_32b_to_64b - Convert a 32b nanoseconds Tx or Rx
 *				       timestamp value to 64b.
 * @cached_phc_time: recently cached copy of PHC time
 * @in_timestamp: Ingress/egress 32b nanoseconds timestamp value
 *
 * Hardware captures timestamps which contain only 32 bits of nominal
 * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
 *
 * Return: Tx timestamp value extended to 64 bits based on cached PHC time.
 */
u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp)
{
	u32 delta, phc_time_lo;
	u64 ns;

	/* Extract the lower 32 bits of the PHC time */
	phc_time_lo = (u32)cached_phc_time;

	/* Calculate the delta between the lower 32bits of the cached PHC
	 * time and the in_timestamp value.
	 */
	delta = in_timestamp - phc_time_lo;

	if (delta > U32_MAX / 2) {
		/* Reverse the delta calculation here */
		delta = phc_time_lo - in_timestamp;
		ns = cached_phc_time - delta;
	} else {
		ns = cached_phc_time + delta;
	}

	return ns;
}

/**
 * idpf_ptp_extend_ts - Convert a 40b timestamp to 64b nanoseconds
 * @vport: Virtual port structure
 * @in_tstamp: Ingress/egress timestamp value
 *
 * It is assumed that the caller verifies the timestamp is valid prior to
 * calling this function.
 *
 * Extract the 32bit nominal nanoseconds and extend them. Use the cached PHC
 * time stored in the device private PTP structure as the basis for timestamp
 * extension.
 *
 * Return: Tx timestamp value extended to 64 bits.
 */
u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u64 in_tstamp)
{
	struct idpf_ptp *ptp = vport->adapter->ptp;
	unsigned long discard_time;

	discard_time = ptp->cached_phc_jiffies + 2 * HZ;

	if (time_is_before_jiffies(discard_time))
		return 0;

	return idpf_ptp_tstamp_extend_32b_to_64b(ptp->cached_phc_time,
						 lower_32_bits(in_tstamp));
}

/**
 * idpf_ptp_request_ts - Request an available Tx timestamp index
 * @tx_q: Transmit queue on which the Tx timestamp is requested
 * @skb: The SKB to associate with this timestamp request
 * @idx: Index of the Tx timestamp latch
 *
 * Request tx timestamp index negotiated during PTP init that will be set into
 * Tx descriptor.
 *
 * Return: 0 and the index that can be provided to Tx descriptor on success,
 * -errno otherwise.
 */
int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
			u32 *idx)
{
	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
	struct list_head *head;

	/* Get the index from the free latches list */
	spin_lock(&tx_q->cached_tstamp_caps->latches_lock);

	head = &tx_q->cached_tstamp_caps->latches_free;
	if (list_empty(head)) {
		spin_unlock(&tx_q->cached_tstamp_caps->latches_lock);
		return -ENOBUFS;
	}

	ptp_tx_tstamp = list_first_entry(head, struct idpf_ptp_tx_tstamp,
					 list_member);
	list_del(&ptp_tx_tstamp->list_member);

	ptp_tx_tstamp->skb = skb_get(skb);
	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;

	/* Move the element to the used latches list */
	list_add(&ptp_tx_tstamp->list_member,
		 &tx_q->cached_tstamp_caps->latches_in_use);
	spin_unlock(&tx_q->cached_tstamp_caps->latches_lock);

	*idx = ptp_tx_tstamp->idx;

	return 0;
}

/**
 * idpf_ptp_set_rx_tstamp - Enable or disable Rx timestamping
 * @vport: Virtual port structure
 * @rx_filter: Receive timestamp filter
 */
static void idpf_ptp_set_rx_tstamp(struct idpf_vport *vport, int rx_filter)
{
	bool enable = true, splitq;

	splitq = idpf_is_queue_model_split(vport->rxq_model);

	if (rx_filter == HWTSTAMP_FILTER_NONE) {
		enable = false;
		vport->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
	} else {
		vport->tstamp_config.rx_filter = HWTSTAMP_FILTER_ALL;
	}

	for (u16 i = 0; i < vport->num_rxq_grp; i++) {
		struct idpf_rxq_group *grp = &vport->rxq_grps[i];
		struct idpf_rx_queue *rx_queue;
		u16 j, num_rxq;

		if (splitq)
			num_rxq = grp->splitq.num_rxq_sets;
		else
			num_rxq = grp->singleq.num_rxq;

		for (j = 0; j < num_rxq; j++) {
			if (splitq)
				rx_queue = &grp->splitq.rxq_sets[j]->rxq;
			else
				rx_queue = grp->singleq.rxqs[j];

			if (enable)
				idpf_queue_set(PTP, rx_queue);
			else
				idpf_queue_clear(PTP, rx_queue);
		}
	}
}

/**
 * idpf_ptp_set_timestamp_mode - Setup driver for requested timestamp mode
 * @vport: Virtual port structure
 * @config: Hwtstamp settings requested or saved
 *
 * Return: 0 on success, -errno otherwise.
 */
int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
				struct kernel_hwtstamp_config *config)
{
	switch (config->tx_type) {
	case HWTSTAMP_TX_OFF:
		break;
	case HWTSTAMP_TX_ON:
		if (!idpf_ptp_is_vport_tx_tstamp_ena(vport))
			return -EINVAL;
		break;
	default:
		return -EINVAL;
	}

	vport->tstamp_config.tx_type = config->tx_type;
	idpf_ptp_set_rx_tstamp(vport, config->rx_filter);
	*config = vport->tstamp_config;

	return 0;
}

/**
 * idpf_tstamp_task - Delayed task to handle Tx tstamps
 * @work: work_struct handle
 */
void idpf_tstamp_task(struct work_struct *work)
{
	struct idpf_vport *vport;

	vport = container_of(work, struct idpf_vport, tstamp_task);

	idpf_ptp_get_tx_tstamp(vport);
}

/**
 * idpf_ptp_do_aux_work - Do PTP periodic work
 * @info: Driver's PTP info structure
 *
 * Return: Number of jiffies to periodic work.
 */
static long idpf_ptp_do_aux_work(struct ptp_clock_info *info)
{
	struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);

	idpf_ptp_update_cached_phctime(adapter);

	return msecs_to_jiffies(500);
}

/**
 * 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->max_adj = adapter->ptp->max_adj;
	info->gettimex64 = idpf_ptp_gettimex64;
	info->settime64 = idpf_ptp_settime64;
	info->adjfine = idpf_ptp_adjfine;
	info->adjtime = idpf_ptp_adjtime;
	info->verify = idpf_ptp_verify_pin;
	info->enable = idpf_ptp_gpio_enable;
	info->do_aux_work = idpf_ptp_do_aux_work;
}

/**
 * idpf_ptp_create_clock - Create PTP clock device for userspace
 * @adapter: Driver specific private structure
 *
 * This function creates a new PTP clock device.
 *
 * Return: 0 on success, -errno otherwise.
 */
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);
	if (IS_ERR(clock)) {
		pci_err(adapter->pdev, "PTP clock creation failed: %pe\n",
			clock);
		return PTR_ERR(clock);
	}

	adapter->ptp->clock = clock;

	return 0;
}

/**
 * idpf_ptp_release_vport_tstamp - Release the Tx timestamps trakcers for a
 *				   given vport.
 * @vport: Virtual port structure
 *
 * Remove the queues and delete lists that tracks Tx timestamp entries for a
 * given vport.
 */
static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
{
	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
	struct list_head *head;

	cancel_work_sync(&vport->tstamp_task);

	/* Remove list with free latches */
	spin_lock_bh(&vport->tx_tstamp_caps->latches_lock);

	head = &vport->tx_tstamp_caps->latches_free;
	list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
		list_del(&ptp_tx_tstamp->list_member);
		kfree(ptp_tx_tstamp);
	}

	/* Remove list with latches in use */
	head = &vport->tx_tstamp_caps->latches_in_use;
	list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
		list_del(&ptp_tx_tstamp->list_member);
		kfree(ptp_tx_tstamp);
	}

	spin_unlock_bh(&vport->tx_tstamp_caps->latches_lock);

	kfree(vport->tx_tstamp_caps);
	vport->tx_tstamp_caps = NULL;
}

/**
 * idpf_ptp_release_tstamp - Release the Tx timestamps trackers
 * @adapter: Driver specific private structure
 *
 * Remove the queues and delete lists that tracks Tx timestamp entries.
 */
static void idpf_ptp_release_tstamp(struct idpf_adapter *adapter)
{
	idpf_for_each_vport(adapter, vport) {
		if (!idpf_ptp_is_vport_tx_tstamp_ena(vport))
			continue;

		idpf_ptp_release_vport_tstamp(vport);
	}
}

/**
 * idpf_ptp_get_txq_tstamp_capability - Verify the timestamping capability
 *					for a given tx queue.
 * @txq: Transmit queue
 *
 * Since performing timestamp flows requires reading the device clock value and
 * the support in the Control Plane, the function checks both factors and
 * summarizes the support for the timestamping.
 *
 * Return: true if the timestamping is supported, false otherwise.
 */
bool idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq)
{
	if (!txq || !txq->cached_tstamp_caps)
		return false;
	else if (txq->cached_tstamp_caps->access)
		return true;
	else
		return false;
}

/**
 * idpf_ptp_init - Initialize PTP hardware clock support
 * @adapter: Driver specific private structure
 *
 * Set up the device for interacting with the PTP hardware clock for all
 * functions. Function will allocate and register a ptp_clock with the
 * PTP_1588_CLOCK infrastructure.
 *
 * Return: 0 on success, -errno otherwise.
 */
int idpf_ptp_init(struct idpf_adapter *adapter)
{
	struct timespec64 ts;
	int err;

	if (!idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_PTP)) {
		pci_dbg(adapter->pdev, "PTP capability is not detected\n");
		return -EOPNOTSUPP;
	}

	adapter->ptp = kzalloc(sizeof(*adapter->ptp), GFP_KERNEL);
	if (!adapter->ptp)
		return -ENOMEM;

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

	if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		ptp_schedule_worker(adapter->ptp->clock, 0);

	/* Write the default increment time value if the clock adjustments
	 * are enabled.
	 */
	if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
		err = idpf_ptp_adj_dev_clk_fine(adapter,
						adapter->ptp->base_incval);
		if (err)
			goto remove_clock;
	}

	/* Write the initial time value if the set time operation is enabled */
	if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
		ts = ktime_to_timespec64(ktime_get_real());
		err = idpf_ptp_settime64(&adapter->ptp->info, &ts);
		if (err)
			goto remove_clock;
	}

	spin_lock_init(&adapter->ptp->read_dev_clk_lock);

	pci_dbg(adapter->pdev, "PTP init successful\n");

	return 0;

remove_clock:
	if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		ptp_cancel_worker_sync(adapter->ptp->clock);

	ptp_clock_unregister(adapter->ptp->clock);
	adapter->ptp->clock = NULL;

free_ptp:
	kfree(adapter->ptp);
	adapter->ptp = NULL;

	return err;
}

/**
 * idpf_ptp_release - Clear PTP hardware clock support
 * @adapter: Driver specific private structure
 */
void idpf_ptp_release(struct idpf_adapter *adapter)
{
	struct idpf_ptp *ptp = adapter->ptp;

	if (!ptp)
		return;

	if (ptp->tx_tstamp_access != IDPF_PTP_NONE &&
	    ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
		idpf_ptp_release_tstamp(adapter);

	if (ptp->clock) {
		if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
			ptp_cancel_worker_sync(adapter->ptp->clock);

		ptp_clock_unregister(ptp->clock);
	}

	kfree(ptp);
	adapter->ptp = NULL;
}