xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/ptp.c (revision 9af1bba4)
19af1bba4SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
29af1bba4SBjoern A. Zeeb /*
39af1bba4SBjoern A. Zeeb  * Copyright (C) 2021 - 2023 Intel Corporation
49af1bba4SBjoern A. Zeeb  */
59af1bba4SBjoern A. Zeeb 
69af1bba4SBjoern A. Zeeb #include "mvm.h"
79af1bba4SBjoern A. Zeeb #include "iwl-debug.h"
89af1bba4SBjoern A. Zeeb #include <linux/timekeeping.h>
99af1bba4SBjoern A. Zeeb #include <linux/math64.h>
109af1bba4SBjoern A. Zeeb 
119af1bba4SBjoern A. Zeeb #define IWL_PTP_GP2_WRAP	0x100000000ULL
129af1bba4SBjoern A. Zeeb #define IWL_PTP_WRAP_TIME	(3600 * HZ)
139af1bba4SBjoern A. Zeeb 
149af1bba4SBjoern A. Zeeb /* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional
159af1bba4SBjoern A. Zeeb  * part, which means that a value of 1 in one of those fields actually means
169af1bba4SBjoern A. Zeeb  * 2^-16 ppm, and 2^16=65536 is 1 ppm.
179af1bba4SBjoern A. Zeeb  */
189af1bba4SBjoern A. Zeeb #define SCALE_FACTOR	65536000000ULL
199af1bba4SBjoern A. Zeeb #define IWL_PTP_WRAP_THRESHOLD_USEC	(5000)
209af1bba4SBjoern A. Zeeb 
219af1bba4SBjoern A. Zeeb #define IWL_PTP_GET_CROSS_TS_NUM	5
229af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_update_new_read(struct iwl_mvm * mvm,u32 gp2)239af1bba4SBjoern A. Zeeb static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2)
249af1bba4SBjoern A. Zeeb {
259af1bba4SBjoern A. Zeeb 	/* If the difference is above the threshold, assume it's a wraparound.
269af1bba4SBjoern A. Zeeb 	 * Otherwise assume it's an old read and ignore it.
279af1bba4SBjoern A. Zeeb 	 */
289af1bba4SBjoern A. Zeeb 	if (gp2 < mvm->ptp_data.last_gp2 &&
299af1bba4SBjoern A. Zeeb 	    mvm->ptp_data.last_gp2 - gp2 < IWL_PTP_WRAP_THRESHOLD_USEC) {
309af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
319af1bba4SBjoern A. Zeeb 			       "PTP: ignore old read (gp2=%u, last_gp2=%u)\n",
329af1bba4SBjoern A. Zeeb 			       gp2, mvm->ptp_data.last_gp2);
339af1bba4SBjoern A. Zeeb 		return;
349af1bba4SBjoern A. Zeeb 	}
359af1bba4SBjoern A. Zeeb 
369af1bba4SBjoern A. Zeeb 	if (gp2 < mvm->ptp_data.last_gp2) {
379af1bba4SBjoern A. Zeeb 		mvm->ptp_data.wrap_counter++;
389af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
399af1bba4SBjoern A. Zeeb 			       "PTP: wraparound detected (new counter=%u)\n",
409af1bba4SBjoern A. Zeeb 			       mvm->ptp_data.wrap_counter);
419af1bba4SBjoern A. Zeeb 	}
429af1bba4SBjoern A. Zeeb 
439af1bba4SBjoern A. Zeeb 	mvm->ptp_data.last_gp2 = gp2;
449af1bba4SBjoern A. Zeeb 	schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME);
459af1bba4SBjoern A. Zeeb }
469af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_get_adj_time(struct iwl_mvm * mvm,u64 base_time_ns)479af1bba4SBjoern A. Zeeb u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time_ns)
489af1bba4SBjoern A. Zeeb {
499af1bba4SBjoern A. Zeeb 	struct ptp_data *data = &mvm->ptp_data;
509af1bba4SBjoern A. Zeeb 	u64 last_gp2_ns = mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
519af1bba4SBjoern A. Zeeb 	u64 res;
529af1bba4SBjoern A. Zeeb 	u64 diff;
539af1bba4SBjoern A. Zeeb 
549af1bba4SBjoern A. Zeeb 	iwl_mvm_ptp_update_new_read(mvm,
559af1bba4SBjoern A. Zeeb 				    div64_u64(base_time_ns, NSEC_PER_USEC));
569af1bba4SBjoern A. Zeeb 
579af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "base_time_ns=%llu, wrap_counter=%u\n",
589af1bba4SBjoern A. Zeeb 		       (unsigned long long)base_time_ns, data->wrap_counter);
599af1bba4SBjoern A. Zeeb 
609af1bba4SBjoern A. Zeeb 	base_time_ns = base_time_ns +
619af1bba4SBjoern A. Zeeb 		(data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC);
629af1bba4SBjoern A. Zeeb 
639af1bba4SBjoern A. Zeeb 	/* It is possible that a GP2 timestamp was received from fw before the
649af1bba4SBjoern A. Zeeb 	 * last scale update. Since we don't know how to scale - ignore it.
659af1bba4SBjoern A. Zeeb 	 */
669af1bba4SBjoern A. Zeeb 	if (base_time_ns < last_gp2_ns) {
679af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Time before scale update - ignore\n");
689af1bba4SBjoern A. Zeeb 		return 0;
699af1bba4SBjoern A. Zeeb 	}
709af1bba4SBjoern A. Zeeb 
719af1bba4SBjoern A. Zeeb 	diff = base_time_ns - last_gp2_ns;
729af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "diff ns=%llu\n", (unsigned long long)diff);
739af1bba4SBjoern A. Zeeb 
749af1bba4SBjoern A. Zeeb 	diff = mul_u64_u64_div_u64(diff, data->scaled_freq,
759af1bba4SBjoern A. Zeeb 				   SCALE_FACTOR);
769af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "scaled diff ns=%llu\n", (unsigned long long)diff);
779af1bba4SBjoern A. Zeeb 
789af1bba4SBjoern A. Zeeb 	res = data->scale_update_adj_time_ns + data->delta + diff;
799af1bba4SBjoern A. Zeeb 
809af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "base=%llu delta=%lld adj=%llu\n",
819af1bba4SBjoern A. Zeeb 		       (unsigned long long)base_time_ns, (long long)data->delta,
829af1bba4SBjoern A. Zeeb 		       (unsigned long long)res);
839af1bba4SBjoern A. Zeeb 	return res;
849af1bba4SBjoern A. Zeeb }
859af1bba4SBjoern A. Zeeb 
869af1bba4SBjoern A. Zeeb static int
iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm * mvm,u32 * gp2,u64 * sys_time)879af1bba4SBjoern A. Zeeb iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time)
889af1bba4SBjoern A. Zeeb {
899af1bba4SBjoern A. Zeeb 	struct iwl_synced_time_cmd synced_time_cmd = {
909af1bba4SBjoern A. Zeeb 		.operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH)
919af1bba4SBjoern A. Zeeb 	};
929af1bba4SBjoern A. Zeeb 	struct iwl_host_cmd cmd = {
939af1bba4SBjoern A. Zeeb 		.id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD),
949af1bba4SBjoern A. Zeeb 		.flags = CMD_WANT_SKB,
959af1bba4SBjoern A. Zeeb 		.data[0] = &synced_time_cmd,
969af1bba4SBjoern A. Zeeb 		.len[0] = sizeof(synced_time_cmd),
979af1bba4SBjoern A. Zeeb 	};
989af1bba4SBjoern A. Zeeb 	struct iwl_synced_time_rsp *resp;
999af1bba4SBjoern A. Zeeb 	struct iwl_rx_packet *pkt;
1009af1bba4SBjoern A. Zeeb 	int ret;
1019af1bba4SBjoern A. Zeeb 	u64 gp2_10ns;
1029af1bba4SBjoern A. Zeeb 
1039af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &cmd);
1049af1bba4SBjoern A. Zeeb 	if (ret)
1059af1bba4SBjoern A. Zeeb 		return ret;
1069af1bba4SBjoern A. Zeeb 
1079af1bba4SBjoern A. Zeeb 	pkt = cmd.resp_pkt;
1089af1bba4SBjoern A. Zeeb 
1099af1bba4SBjoern A. Zeeb 	if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) {
1109af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "PTP: Invalid command response\n");
1119af1bba4SBjoern A. Zeeb 		iwl_free_resp(&cmd);
1129af1bba4SBjoern A. Zeeb 		return -EIO;
1139af1bba4SBjoern A. Zeeb 	}
1149af1bba4SBjoern A. Zeeb 
1159af1bba4SBjoern A. Zeeb 	resp = (void *)pkt->data;
1169af1bba4SBjoern A. Zeeb 
1179af1bba4SBjoern A. Zeeb 	gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 |
1189af1bba4SBjoern A. Zeeb 		le32_to_cpu(resp->gp2_timestamp_lo);
1199af1bba4SBjoern A. Zeeb 	*gp2 = div_u64(gp2_10ns, 100);
1209af1bba4SBjoern A. Zeeb 
1219af1bba4SBjoern A. Zeeb 	*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
1229af1bba4SBjoern A. Zeeb 		le32_to_cpu(resp->platform_timestamp_lo);
1239af1bba4SBjoern A. Zeeb 
1249af1bba4SBjoern A. Zeeb 	return ret;
1259af1bba4SBjoern A. Zeeb }
1269af1bba4SBjoern A. Zeeb 
iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm * mvm,ktime_t * sys_time,u32 * gp2)1279af1bba4SBjoern A. Zeeb static void iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm *mvm,
1289af1bba4SBjoern A. Zeeb 						ktime_t *sys_time, u32 *gp2)
1299af1bba4SBjoern A. Zeeb {
1309af1bba4SBjoern A. Zeeb 	u64 diff = 0, new_diff;
1319af1bba4SBjoern A. Zeeb 	u64 tmp_sys_time;
1329af1bba4SBjoern A. Zeeb 	u32 tmp_gp2;
1339af1bba4SBjoern A. Zeeb 	int i;
1349af1bba4SBjoern A. Zeeb 
1359af1bba4SBjoern A. Zeeb 	for (i = 0; i < IWL_PTP_GET_CROSS_TS_NUM; i++) {
1369af1bba4SBjoern A. Zeeb 		iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &tmp_gp2, NULL,
1379af1bba4SBjoern A. Zeeb 				      &tmp_sys_time);
1389af1bba4SBjoern A. Zeeb 		new_diff = tmp_sys_time - ((u64)tmp_gp2 * NSEC_PER_USEC);
1399af1bba4SBjoern A. Zeeb 		if (!diff || new_diff < diff) {
1409af1bba4SBjoern A. Zeeb 			*sys_time = tmp_sys_time;
1419af1bba4SBjoern A. Zeeb 			*gp2 = tmp_gp2;
1429af1bba4SBjoern A. Zeeb 			diff = new_diff;
1439af1bba4SBjoern A. Zeeb 			IWL_DEBUG_INFO(mvm, "PTP: new times: gp2=%u sys=%lld\n",
1449af1bba4SBjoern A. Zeeb 				       *gp2, *sys_time);
1459af1bba4SBjoern A. Zeeb 		}
1469af1bba4SBjoern A. Zeeb 	}
1479af1bba4SBjoern A. Zeeb }
1489af1bba4SBjoern A. Zeeb 
1499af1bba4SBjoern A. Zeeb static int
iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)1509af1bba4SBjoern A. Zeeb iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
1519af1bba4SBjoern A. Zeeb 			       struct system_device_crosststamp *xtstamp)
1529af1bba4SBjoern A. Zeeb {
1539af1bba4SBjoern A. Zeeb 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
1549af1bba4SBjoern A. Zeeb 					   ptp_data.ptp_clock_info);
1559af1bba4SBjoern A. Zeeb 	int ret = 0;
1569af1bba4SBjoern A. Zeeb 	/* Raw value read from GP2 register in usec */
1579af1bba4SBjoern A. Zeeb 	u32 gp2;
1589af1bba4SBjoern A. Zeeb 	/* GP2 value in ns*/
1599af1bba4SBjoern A. Zeeb 	s64 gp2_ns;
1609af1bba4SBjoern A. Zeeb 	/* System (wall) time */
1619af1bba4SBjoern A. Zeeb 	ktime_t sys_time;
1629af1bba4SBjoern A. Zeeb 
1639af1bba4SBjoern A. Zeeb 	memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
1649af1bba4SBjoern A. Zeeb 
1659af1bba4SBjoern A. Zeeb 	if (!mvm->ptp_data.ptp_clock) {
1669af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "No PHC clock registered\n");
1679af1bba4SBjoern A. Zeeb 		return -ENODEV;
1689af1bba4SBjoern A. Zeeb 	}
1699af1bba4SBjoern A. Zeeb 
1709af1bba4SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
1719af1bba4SBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) {
1729af1bba4SBjoern A. Zeeb 		ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time);
1739af1bba4SBjoern A. Zeeb 
1749af1bba4SBjoern A. Zeeb 		if (ret)
1759af1bba4SBjoern A. Zeeb 			goto out;
1769af1bba4SBjoern A. Zeeb 	} else {
1779af1bba4SBjoern A. Zeeb 		iwl_mvm_phc_get_crosstimestamp_loop(mvm, &sys_time, &gp2);
1789af1bba4SBjoern A. Zeeb 	}
1799af1bba4SBjoern A. Zeeb 
1809af1bba4SBjoern A. Zeeb 	gp2_ns = iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC);
1819af1bba4SBjoern A. Zeeb 
1829af1bba4SBjoern A. Zeeb 	IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
1839af1bba4SBjoern A. Zeeb 		 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
1849af1bba4SBjoern A. Zeeb 
1859af1bba4SBjoern A. Zeeb 	/* System monotonic raw time is not used */
1869af1bba4SBjoern A. Zeeb 	xtstamp->device = (ktime_t)gp2_ns;
1879af1bba4SBjoern A. Zeeb 	xtstamp->sys_realtime = sys_time;
1889af1bba4SBjoern A. Zeeb 
1899af1bba4SBjoern A. Zeeb out:
1909af1bba4SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
1919af1bba4SBjoern A. Zeeb 	return ret;
1929af1bba4SBjoern A. Zeeb }
1939af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_work(struct work_struct * wk)1949af1bba4SBjoern A. Zeeb static void iwl_mvm_ptp_work(struct work_struct *wk)
1959af1bba4SBjoern A. Zeeb {
1969af1bba4SBjoern A. Zeeb 	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
1979af1bba4SBjoern A. Zeeb 					   ptp_data.dwork.work);
1989af1bba4SBjoern A. Zeeb 	u32 gp2;
1999af1bba4SBjoern A. Zeeb 
2009af1bba4SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
2019af1bba4SBjoern A. Zeeb 	gp2 = iwl_mvm_get_systime(mvm);
2029af1bba4SBjoern A. Zeeb 	iwl_mvm_ptp_update_new_read(mvm, gp2);
2039af1bba4SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
2049af1bba4SBjoern A. Zeeb }
2059af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)2069af1bba4SBjoern A. Zeeb static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp,
2079af1bba4SBjoern A. Zeeb 			       struct timespec64 *ts)
2089af1bba4SBjoern A. Zeeb {
2099af1bba4SBjoern A. Zeeb 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
2109af1bba4SBjoern A. Zeeb 					   ptp_data.ptp_clock_info);
2119af1bba4SBjoern A. Zeeb 	u64 gp2;
2129af1bba4SBjoern A. Zeeb 	u64 ns;
2139af1bba4SBjoern A. Zeeb 
2149af1bba4SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
2159af1bba4SBjoern A. Zeeb 	gp2 = iwl_mvm_get_systime(mvm);
2169af1bba4SBjoern A. Zeeb 	ns = iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
2179af1bba4SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
2189af1bba4SBjoern A. Zeeb 
2199af1bba4SBjoern A. Zeeb 	*ts = ns_to_timespec64(ns);
2209af1bba4SBjoern A. Zeeb 	return 0;
2219af1bba4SBjoern A. Zeeb }
2229af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)2239af1bba4SBjoern A. Zeeb static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
2249af1bba4SBjoern A. Zeeb {
2259af1bba4SBjoern A. Zeeb 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
2269af1bba4SBjoern A. Zeeb 					   ptp_data.ptp_clock_info);
2279af1bba4SBjoern A. Zeeb 	struct ptp_data *data = container_of(ptp, struct ptp_data,
2289af1bba4SBjoern A. Zeeb 					     ptp_clock_info);
2299af1bba4SBjoern A. Zeeb 
2309af1bba4SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
2319af1bba4SBjoern A. Zeeb 	data->delta += delta;
2329af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta,
2339af1bba4SBjoern A. Zeeb 		       (long long)data->delta);
2349af1bba4SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
2359af1bba4SBjoern A. Zeeb 	return 0;
2369af1bba4SBjoern A. Zeeb }
2379af1bba4SBjoern A. Zeeb 
iwl_mvm_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)2389af1bba4SBjoern A. Zeeb static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
2399af1bba4SBjoern A. Zeeb {
2409af1bba4SBjoern A. Zeeb 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
2419af1bba4SBjoern A. Zeeb 					   ptp_data.ptp_clock_info);
2429af1bba4SBjoern A. Zeeb 	struct ptp_data *data = &mvm->ptp_data;
2439af1bba4SBjoern A. Zeeb 	u32 gp2;
2449af1bba4SBjoern A. Zeeb 
2459af1bba4SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
2469af1bba4SBjoern A. Zeeb 
2479af1bba4SBjoern A. Zeeb 	/* Must call _iwl_mvm_ptp_get_adj_time() before updating
2489af1bba4SBjoern A. Zeeb 	 * data->scale_update_gp2 or data->scaled_freq since
2499af1bba4SBjoern A. Zeeb 	 * scale_update_adj_time_ns should reflect the previous scaled_freq.
2509af1bba4SBjoern A. Zeeb 	 */
2519af1bba4SBjoern A. Zeeb 	gp2 = iwl_mvm_get_systime(mvm);
2529af1bba4SBjoern A. Zeeb 	data->scale_update_adj_time_ns =
2539af1bba4SBjoern A. Zeeb 		iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
2549af1bba4SBjoern A. Zeeb 	data->scale_update_gp2 = gp2;
2559af1bba4SBjoern A. Zeeb 	data->wrap_counter = 0;
2569af1bba4SBjoern A. Zeeb 	data->delta = 0;
2579af1bba4SBjoern A. Zeeb 
2589af1bba4SBjoern A. Zeeb 	data->scaled_freq = SCALE_FACTOR + scaled_ppm;
2599af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n",
2609af1bba4SBjoern A. Zeeb 		       scaled_ppm, (unsigned long long)data->scaled_freq);
2619af1bba4SBjoern A. Zeeb 
2629af1bba4SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
2639af1bba4SBjoern A. Zeeb 	return 0;
2649af1bba4SBjoern A. Zeeb }
2659af1bba4SBjoern A. Zeeb 
2669af1bba4SBjoern A. Zeeb /* iwl_mvm_ptp_init - initialize PTP for devices which support it.
2679af1bba4SBjoern A. Zeeb  * @mvm: internal mvm structure, see &struct iwl_mvm.
2689af1bba4SBjoern A. Zeeb  *
2699af1bba4SBjoern A. Zeeb  * Performs the required steps for enabling PTP support.
2709af1bba4SBjoern A. Zeeb  */
iwl_mvm_ptp_init(struct iwl_mvm * mvm)2719af1bba4SBjoern A. Zeeb void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
2729af1bba4SBjoern A. Zeeb {
2739af1bba4SBjoern A. Zeeb 	/* Warn if the interface already has a ptp_clock defined */
2749af1bba4SBjoern A. Zeeb 	if (WARN_ON(mvm->ptp_data.ptp_clock))
2759af1bba4SBjoern A. Zeeb 		return;
2769af1bba4SBjoern A. Zeeb 
2779af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
2789af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
2799af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.getcrosststamp =
2809af1bba4SBjoern A. Zeeb 					iwl_mvm_phc_get_crosstimestamp;
2819af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine;
2829af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime;
2839af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime;
2849af1bba4SBjoern A. Zeeb 	mvm->ptp_data.scaled_freq = SCALE_FACTOR;
2859af1bba4SBjoern A. Zeeb 
2869af1bba4SBjoern A. Zeeb 	/* Give a short 'friendly name' to identify the PHC clock */
2879af1bba4SBjoern A. Zeeb 	snprintf(mvm->ptp_data.ptp_clock_info.name,
2889af1bba4SBjoern A. Zeeb 		 sizeof(mvm->ptp_data.ptp_clock_info.name),
2899af1bba4SBjoern A. Zeeb 		 "%s", "iwlwifi-PTP");
2909af1bba4SBjoern A. Zeeb 
2919af1bba4SBjoern A. Zeeb 	INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
2929af1bba4SBjoern A. Zeeb 
2939af1bba4SBjoern A. Zeeb 	mvm->ptp_data.ptp_clock =
2949af1bba4SBjoern A. Zeeb 		ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
2959af1bba4SBjoern A. Zeeb 
2969af1bba4SBjoern A. Zeeb 	if (IS_ERR(mvm->ptp_data.ptp_clock)) {
2979af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
2989af1bba4SBjoern A. Zeeb 			PTR_ERR(mvm->ptp_data.ptp_clock));
2999af1bba4SBjoern A. Zeeb 		mvm->ptp_data.ptp_clock = NULL;
3009af1bba4SBjoern A. Zeeb 	} else if (mvm->ptp_data.ptp_clock) {
3019af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
3029af1bba4SBjoern A. Zeeb 			 mvm->ptp_data.ptp_clock_info.name,
3039af1bba4SBjoern A. Zeeb 			 ptp_clock_index(mvm->ptp_data.ptp_clock));
3049af1bba4SBjoern A. Zeeb 	}
3059af1bba4SBjoern A. Zeeb }
3069af1bba4SBjoern A. Zeeb 
3079af1bba4SBjoern A. Zeeb /* iwl_mvm_ptp_remove - disable PTP device.
3089af1bba4SBjoern A. Zeeb  * @mvm: internal mvm structure, see &struct iwl_mvm.
3099af1bba4SBjoern A. Zeeb  *
3109af1bba4SBjoern A. Zeeb  * Disable PTP support.
3119af1bba4SBjoern A. Zeeb  */
iwl_mvm_ptp_remove(struct iwl_mvm * mvm)3129af1bba4SBjoern A. Zeeb void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
3139af1bba4SBjoern A. Zeeb {
3149af1bba4SBjoern A. Zeeb 	if (mvm->ptp_data.ptp_clock) {
3159af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
3169af1bba4SBjoern A. Zeeb 			 mvm->ptp_data.ptp_clock_info.name,
3179af1bba4SBjoern A. Zeeb 			 ptp_clock_index(mvm->ptp_data.ptp_clock));
3189af1bba4SBjoern A. Zeeb 
3199af1bba4SBjoern A. Zeeb 		ptp_clock_unregister(mvm->ptp_data.ptp_clock);
3209af1bba4SBjoern A. Zeeb 		mvm->ptp_data.ptp_clock = NULL;
3219af1bba4SBjoern A. Zeeb 		memset(&mvm->ptp_data.ptp_clock_info, 0,
3229af1bba4SBjoern A. Zeeb 		       sizeof(mvm->ptp_data.ptp_clock_info));
3239af1bba4SBjoern A. Zeeb 		mvm->ptp_data.last_gp2 = 0;
3249af1bba4SBjoern A. Zeeb 		cancel_delayed_work_sync(&mvm->ptp_data.dwork);
3259af1bba4SBjoern A. Zeeb 	}
3269af1bba4SBjoern A. Zeeb }
327