xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c (revision fab183d632628381b466a41479489541ac0e29a0)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2021 - 2023, 2025-2026 Intel Corporation
4  */
5 
6 #include "mvm.h"
7 #include "iwl-debug.h"
8 #include <linux/timekeeping.h>
9 #include <linux/math64.h>
10 
11 #define IWL_PTP_GP2_WRAP	0x100000000ULL
12 #define IWL_PTP_WRAP_TIME	(3600 * HZ)
13 
14 /* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional
15  * part, which means that a value of 1 in one of those fields actually means
16  * 2^-16 ppm, and 2^16=65536 is 1 ppm.
17  */
18 #define SCALE_FACTOR	65536000000ULL
19 #define IWL_PTP_WRAP_THRESHOLD_USEC	(5000)
20 
21 #define IWL_PTP_GET_CROSS_TS_NUM	5
22 
iwl_mvm_ptp_update_new_read(struct iwl_mvm * mvm,u32 gp2)23 static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2)
24 {
25 	/* If the difference is above the threshold, assume it's a wraparound.
26 	 * Otherwise assume it's an old read and ignore it.
27 	 */
28 	if (gp2 < mvm->ptp_data.last_gp2 &&
29 	    mvm->ptp_data.last_gp2 - gp2 < IWL_PTP_WRAP_THRESHOLD_USEC) {
30 		IWL_DEBUG_INFO(mvm,
31 			       "PTP: ignore old read (gp2=%u, last_gp2=%u)\n",
32 			       gp2, mvm->ptp_data.last_gp2);
33 		return;
34 	}
35 
36 	if (gp2 < mvm->ptp_data.last_gp2) {
37 		mvm->ptp_data.wrap_counter++;
38 		IWL_DEBUG_INFO(mvm,
39 			       "PTP: wraparound detected (new counter=%u)\n",
40 			       mvm->ptp_data.wrap_counter);
41 	}
42 
43 	mvm->ptp_data.last_gp2 = gp2;
44 	schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME);
45 }
46 
iwl_mvm_ptp_get_adj_time(struct iwl_mvm * mvm,u64 base_time_ns)47 u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time_ns)
48 {
49 	struct ptp_data *data = &mvm->ptp_data;
50 	u64 last_gp2_ns = mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
51 	u64 res;
52 	u64 diff;
53 
54 	iwl_mvm_ptp_update_new_read(mvm,
55 				    div64_u64(base_time_ns, NSEC_PER_USEC));
56 
57 	IWL_DEBUG_INFO(mvm, "base_time_ns=%llu, wrap_counter=%u\n",
58 		       (unsigned long long)base_time_ns, data->wrap_counter);
59 
60 	base_time_ns = base_time_ns +
61 		(data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC);
62 
63 	/* It is possible that a GP2 timestamp was received from fw before the
64 	 * last scale update. Since we don't know how to scale - ignore it.
65 	 */
66 	if (base_time_ns < last_gp2_ns) {
67 		IWL_DEBUG_INFO(mvm, "Time before scale update - ignore\n");
68 		return 0;
69 	}
70 
71 	diff = base_time_ns - last_gp2_ns;
72 	IWL_DEBUG_INFO(mvm, "diff ns=%llu\n", (unsigned long long)diff);
73 
74 	diff = mul_u64_u64_div_u64(diff, data->scaled_freq,
75 				   SCALE_FACTOR);
76 	IWL_DEBUG_INFO(mvm, "scaled diff ns=%llu\n", (unsigned long long)diff);
77 
78 	res = data->scale_update_adj_time_ns + data->delta + diff;
79 
80 	IWL_DEBUG_INFO(mvm, "base=%llu delta=%lld adj=%llu\n",
81 		       (unsigned long long)base_time_ns, (long long)data->delta,
82 		       (unsigned long long)res);
83 	return res;
84 }
85 
86 static int
iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm * mvm,u32 * gp2,u64 * sys_time)87 iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time)
88 {
89 	struct iwl_synced_time_cmd synced_time_cmd = {
90 		.operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH)
91 	};
92 	struct iwl_host_cmd cmd = {
93 		.id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD),
94 		.flags = CMD_WANT_SKB,
95 		.data[0] = &synced_time_cmd,
96 		.len[0] = sizeof(synced_time_cmd),
97 	};
98 	struct iwl_synced_time_rsp *resp;
99 	struct iwl_rx_packet *pkt;
100 	int ret;
101 	u64 gp2_10ns;
102 
103 	ret = iwl_mvm_send_cmd(mvm, &cmd);
104 	if (ret)
105 		return ret;
106 
107 	pkt = cmd.resp_pkt;
108 
109 	if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) {
110 		IWL_ERR(mvm, "PTP: Invalid command response\n");
111 		iwl_free_resp(&cmd);
112 		return -EIO;
113 	}
114 
115 	resp = (void *)pkt->data;
116 
117 	gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 |
118 		le32_to_cpu(resp->gp2_timestamp_lo);
119 	*gp2 = div_u64(gp2_10ns, 100);
120 
121 	*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
122 		le32_to_cpu(resp->platform_timestamp_lo);
123 
124 	iwl_free_resp(&cmd);
125 
126 	return ret;
127 }
128 
iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm * mvm,ktime_t * sys_time,u32 * gp2)129 static void iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm *mvm,
130 						ktime_t *sys_time, u32 *gp2)
131 {
132 	u64 diff = 0, new_diff;
133 	u64 tmp_sys_time;
134 	u32 tmp_gp2;
135 	int i;
136 
137 	for (i = 0; i < IWL_PTP_GET_CROSS_TS_NUM; i++) {
138 		iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &tmp_gp2, NULL,
139 				      &tmp_sys_time);
140 		new_diff = tmp_sys_time - ((u64)tmp_gp2 * NSEC_PER_USEC);
141 		if (!diff || new_diff < diff) {
142 			*sys_time = tmp_sys_time;
143 			*gp2 = tmp_gp2;
144 			diff = new_diff;
145 			IWL_DEBUG_INFO(mvm, "PTP: new times: gp2=%u sys=%lld\n",
146 				       *gp2, *sys_time);
147 		}
148 	}
149 }
150 
151 static int
iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)152 iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
153 			       struct system_device_crosststamp *xtstamp)
154 {
155 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
156 					   ptp_data.ptp_clock_info);
157 	int ret = 0;
158 	/* Raw value read from GP2 register in usec */
159 	u32 gp2;
160 	/* GP2 value in ns*/
161 	s64 gp2_ns;
162 	/* System (wall) time */
163 	ktime_t sys_time;
164 
165 	if (!mvm->ptp_data.ptp_clock) {
166 		IWL_ERR(mvm, "No PHC clock registered\n");
167 		return -ENODEV;
168 	}
169 
170 	if (xtstamp->clock_id != CLOCK_REALTIME)
171 		return -ENOTSUPP;
172 
173 	mutex_lock(&mvm->mutex);
174 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) {
175 		ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time);
176 
177 		if (ret)
178 			goto out;
179 	} else {
180 		iwl_mvm_phc_get_crosstimestamp_loop(mvm, &sys_time, &gp2);
181 	}
182 
183 	gp2_ns = iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC);
184 
185 	IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
186 		 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
187 
188 	/* System monotonic raw time is not used */
189 	xtstamp->device = (ktime_t)gp2_ns;
190 	xtstamp->sys_systime = sys_time;
191 
192 out:
193 	mutex_unlock(&mvm->mutex);
194 	return ret;
195 }
196 
iwl_mvm_ptp_work(struct work_struct * wk)197 static void iwl_mvm_ptp_work(struct work_struct *wk)
198 {
199 	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
200 					   ptp_data.dwork.work);
201 	u32 gp2;
202 
203 	mutex_lock(&mvm->mutex);
204 	gp2 = iwl_mvm_get_systime(mvm);
205 	iwl_mvm_ptp_update_new_read(mvm, gp2);
206 	mutex_unlock(&mvm->mutex);
207 }
208 
iwl_mvm_ptp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)209 static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp,
210 			       struct timespec64 *ts)
211 {
212 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
213 					   ptp_data.ptp_clock_info);
214 	u64 gp2;
215 	u64 ns;
216 
217 	mutex_lock(&mvm->mutex);
218 	gp2 = iwl_mvm_get_systime(mvm);
219 	ns = iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
220 	mutex_unlock(&mvm->mutex);
221 
222 	*ts = ns_to_timespec64(ns);
223 	return 0;
224 }
225 
iwl_mvm_ptp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)226 static int iwl_mvm_ptp_settime(struct ptp_clock_info *ptp,
227 			       const struct timespec64 *ts)
228 {
229 	return -EOPNOTSUPP;
230 }
231 
iwl_mvm_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)232 static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
233 {
234 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
235 					   ptp_data.ptp_clock_info);
236 	struct ptp_data *data = container_of(ptp, struct ptp_data,
237 					     ptp_clock_info);
238 
239 	mutex_lock(&mvm->mutex);
240 	data->delta += delta;
241 	IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta,
242 		       (long long)data->delta);
243 	mutex_unlock(&mvm->mutex);
244 	return 0;
245 }
246 
iwl_mvm_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)247 static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
248 {
249 	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
250 					   ptp_data.ptp_clock_info);
251 	struct ptp_data *data = &mvm->ptp_data;
252 	u32 gp2;
253 
254 	mutex_lock(&mvm->mutex);
255 
256 	/* Must call _iwl_mvm_ptp_get_adj_time() before updating
257 	 * data->scale_update_gp2 or data->scaled_freq since
258 	 * scale_update_adj_time_ns should reflect the previous scaled_freq.
259 	 */
260 	gp2 = iwl_mvm_get_systime(mvm);
261 	data->scale_update_adj_time_ns =
262 		iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
263 	data->scale_update_gp2 = gp2;
264 	data->wrap_counter = 0;
265 	data->delta = 0;
266 
267 	data->scaled_freq = SCALE_FACTOR + scaled_ppm;
268 	IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n",
269 		       scaled_ppm, (unsigned long long)data->scaled_freq);
270 
271 	mutex_unlock(&mvm->mutex);
272 	return 0;
273 }
274 
275 /* iwl_mvm_ptp_init - initialize PTP for devices which support it.
276  * @mvm: internal mvm structure, see &struct iwl_mvm.
277  *
278  * Performs the required steps for enabling PTP support.
279  */
iwl_mvm_ptp_init(struct iwl_mvm * mvm)280 void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
281 {
282 	/* Warn if the interface already has a ptp_clock defined */
283 	if (WARN_ON(mvm->ptp_data.ptp_clock))
284 		return;
285 
286 	mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
287 	mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
288 	mvm->ptp_data.ptp_clock_info.getcrosststamp =
289 					iwl_mvm_phc_get_crosstimestamp;
290 	mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine;
291 	mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime;
292 	mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime;
293 	mvm->ptp_data.ptp_clock_info.settime64 = iwl_mvm_ptp_settime;
294 	mvm->ptp_data.scaled_freq = SCALE_FACTOR;
295 
296 	/* Give a short 'friendly name' to identify the PHC clock */
297 	snprintf(mvm->ptp_data.ptp_clock_info.name,
298 		 sizeof(mvm->ptp_data.ptp_clock_info.name),
299 		 "%s", "iwlwifi-PTP");
300 
301 	INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
302 
303 	mvm->ptp_data.ptp_clock =
304 		ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
305 
306 	if (IS_ERR(mvm->ptp_data.ptp_clock)) {
307 		IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
308 			PTR_ERR(mvm->ptp_data.ptp_clock));
309 		mvm->ptp_data.ptp_clock = NULL;
310 	} else if (!mvm->ptp_data.ptp_clock) {
311 		IWL_DEBUG_INFO(mvm, "PTP module unavailable on this kernel\n");
312 	} else {
313 		IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
314 			       mvm->ptp_data.ptp_clock_info.name,
315 			       ptp_clock_index(mvm->ptp_data.ptp_clock));
316 	}
317 }
318 
319 /* iwl_mvm_ptp_remove - disable PTP device.
320  * @mvm: internal mvm structure, see &struct iwl_mvm.
321  *
322  * Disable PTP support.
323  */
iwl_mvm_ptp_remove(struct iwl_mvm * mvm)324 void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
325 {
326 	if (mvm->ptp_data.ptp_clock) {
327 		IWL_DEBUG_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
328 			       mvm->ptp_data.ptp_clock_info.name,
329 			       ptp_clock_index(mvm->ptp_data.ptp_clock));
330 
331 		cancel_delayed_work_sync(&mvm->ptp_data.dwork);
332 		ptp_clock_unregister(mvm->ptp_data.ptp_clock);
333 		mvm->ptp_data.ptp_clock = NULL;
334 		memset(&mvm->ptp_data.ptp_clock_info, 0,
335 		       sizeof(mvm->ptp_data.ptp_clock_info));
336 		mvm->ptp_data.last_gp2 = 0;
337 	}
338 }
339