drivers/net/wireless/intel/iwlwifi/mld/ptp.c

Source file repositories/reference/linux-study-clean/drivers/net/wireless/intel/iwlwifi/mld/ptp.c

File Facts

System
Linux kernel
Corpus path
drivers/net/wireless/intel/iwlwifi/mld/ptp.c
Extension
.c
Size
9521 bytes
Lines
332
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
 * Copyright (C) 2025 Intel Corporation
 */

#include "mld.h"
#include "iwl-debug.h"
#include "hcmd.h"
#include "ptp.h"
#include <linux/timekeeping.h>

/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional
 * part, which means that a value of 1 in one of those fields actually means
 * 2^-16 ppm, and 2^16=65536 is 1 ppm.
 */
#define PTP_SCALE_FACTOR	65536000000ULL

#define IWL_PTP_GP2_WRAP	0x100000000ULL
#define IWL_PTP_WRAP_TIME	(3600 * HZ)
#define IWL_PTP_WRAP_THRESHOLD_USEC	(5000)

static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2)
{
	*gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr);

	if (*gp2 == 0x5a5a5a5a)
		return -EINVAL;

	return 0;
}

static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2)
{
	IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n",
		      mld->ptp_data.last_gp2, gp2);

	/* If the difference is above the threshold, assume it's a wraparound.
	 * Otherwise assume it's an old read and ignore it.
	 */
	if (gp2 < mld->ptp_data.last_gp2) {
		if (mld->ptp_data.last_gp2 - gp2 <
		    IWL_PTP_WRAP_THRESHOLD_USEC) {
			IWL_DEBUG_PTP(mld,
				      "PTP: ignore old read (gp2=%u, last_gp2=%u)\n",
				      gp2, mld->ptp_data.last_gp2);
			return;
		}

		mld->ptp_data.wrap_counter++;
		IWL_DEBUG_PTP(mld,
			      "PTP: wraparound detected (new counter=%u)\n",
			      mld->ptp_data.wrap_counter);
	}

	mld->ptp_data.last_gp2 = gp2;
	schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME);
}

u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns)
{
	struct ptp_data *data = &mld->ptp_data;
	u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
	u64 res;
	u64 diff;
	s64 scaled_diff;

	lockdep_assert_held(&data->lock);

	iwl_mld_ptp_update_new_read(mld,
				    div64_u64(base_time_ns, NSEC_PER_USEC));

	base_time_ns = base_time_ns +
		(data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC);

	/* It is possible that a GP2 timestamp was received from fw before the
	 * last scale update.
	 */
	if (base_time_ns < scale_time_gp2_ns) {
		diff = scale_time_gp2_ns - base_time_ns;
		scaled_diff = -mul_u64_u64_div_u64(diff,
						   data->scaled_freq,
						   PTP_SCALE_FACTOR);
	} else {
		diff = base_time_ns - scale_time_gp2_ns;
		scaled_diff = mul_u64_u64_div_u64(diff,
						  data->scaled_freq,
						  PTP_SCALE_FACTOR);
	}

	IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n",

Annotation

Implementation Notes