File Index
Files and directories
Sign in
FILE

ptp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/ptp.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2/*
  3 * Copyright (C) 2025 Intel Corporation
  4 */
  5
  6#include "mld.h"
  7#include "iwl-debug.h"
  8#include "hcmd.h"
  9#include "ptp.h"
 10#include <linux/timekeeping.h>
 11
 12/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional
 13 * part, which means that a value of 1 in one of those fields actually means
 14 * 2^-16 ppm, and 2^16=65536 is 1 ppm.
 15 */
 16#define PTP_SCALE_FACTOR	65536000000ULL
 17
 18#define IWL_PTP_GP2_WRAP	0x100000000ULL
 19#define IWL_PTP_WRAP_TIME	(3600 * HZ)
 20#define IWL_PTP_WRAP_THRESHOLD_USEC	(5000)
 21
 22static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2)
 23{
 24	*gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr);
 25
 26	if (*gp2 == 0x5a5a5a5a)
 27		return -EINVAL;
 28
 29	return 0;
 30}
 31
 32static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2)
 33{
 34	IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n",
 35		      mld->ptp_data.last_gp2, gp2);
 36
 37	/* If the difference is above the threshold, assume it's a wraparound.
 38	 * Otherwise assume it's an old read and ignore it.
 39	 */
 40	if (gp2 < mld->ptp_data.last_gp2) {
 41		if (mld->ptp_data.last_gp2 - gp2 <
 42		    IWL_PTP_WRAP_THRESHOLD_USEC) {
 43			IWL_DEBUG_PTP(mld,
 44				      "PTP: ignore old read (gp2=%u, last_gp2=%u)\n",
 45				      gp2, mld->ptp_data.last_gp2);
 46			return;
 47		}
 48
 49		mld->ptp_data.wrap_counter++;
 50		IWL_DEBUG_PTP(mld,
 51			      "PTP: wraparound detected (new counter=%u)\n",
 52			      mld->ptp_data.wrap_counter);
 53	}
 54
 55	mld->ptp_data.last_gp2 = gp2;
 56	schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME);
 57}
 58
 59u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns)
 60{
 61	struct ptp_data *data = &mld->ptp_data;
 62	u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC;
 63	u64 res;
 64	u64 diff;
 65	s64 scaled_diff;
 66
 67	lockdep_assert_held(&data->lock);
 68
 69	iwl_mld_ptp_update_new_read(mld,
 70				    div64_u64(base_time_ns, NSEC_PER_USEC));
 71
 72	base_time_ns = base_time_ns +
 73		(data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC);
 74
 75	/* It is possible that a GP2 timestamp was received from fw before the
 76	 * last scale update.
 77	 */
 78	if (base_time_ns < scale_time_gp2_ns) {
 79		diff = scale_time_gp2_ns - base_time_ns;
 80		scaled_diff = -mul_u64_u64_div_u64(diff,
 81						   data->scaled_freq,
 82						   PTP_SCALE_FACTOR);
 83	} else {
 84		diff = base_time_ns - scale_time_gp2_ns;
 85		scaled_diff = mul_u64_u64_div_u64(diff,
 86						  data->scaled_freq,
 87						  PTP_SCALE_FACTOR);
 88	}
 89
 90	IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n",
 91		      (unsigned long long)base_time_ns,
 92		      (unsigned long long)diff, (long long)scaled_diff);
 93
 94	res = data->scale_update_adj_time_ns + data->delta + scaled_diff;
 95
 96	IWL_DEBUG_PTP(mld, "scale_update_ns=%llu delta=%lld adj=%llu\n",
 97		      (unsigned long long)data->scale_update_adj_time_ns,
 98		      (long long)data->delta, (unsigned long long)res);
 99	return res;
100}
101
102static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp,
103			       struct timespec64 *ts)
104{
105	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
106					   ptp_data.ptp_clock_info);
107	struct ptp_data *data = &mld->ptp_data;
108	u32 gp2;
109	u64 ns;
110
111	if (iwl_mld_get_systime(mld, &gp2)) {
112		IWL_DEBUG_PTP(mld, "PTP: gettime: failed to read systime\n");
113		return -EIO;
114	}
115
116	spin_lock_bh(&data->lock);
117	ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
118	spin_unlock_bh(&data->lock);
119
120	*ts = ns_to_timespec64(ns);
121	return 0;
122}
123
124static int iwl_mld_ptp_settime(struct ptp_clock_info *ptp,
125			       const struct timespec64 *ts)
126{
127	return -EOPNOTSUPP;
128}
129
130static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
131{
132	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
133					   ptp_data.ptp_clock_info);
134	struct ptp_data *data = &mld->ptp_data;
135
136	spin_lock_bh(&data->lock);
137	data->delta += delta;
138	IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta,
139		      (long long)data->delta);
140	spin_unlock_bh(&data->lock);
141	return 0;
142}
143
144static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
145{
146	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
147					   ptp_data.ptp_clock_info);
148	struct ptp_data *data = &mld->ptp_data;
149	u32 gp2;
150
151	/* Must call iwl_mld_ptp_get_adj_time() before updating
152	 * data->scale_update_gp2 or data->scaled_freq since
153	 * scale_update_adj_time_ns should reflect the previous scaled_freq.
154	 */
155	if (iwl_mld_get_systime(mld, &gp2)) {
156		IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n");
157		return -EBUSY;
158	}
159
160	spin_lock_bh(&data->lock);
161	data->scale_update_adj_time_ns =
162		iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC);
163	data->scale_update_gp2 = gp2;
164
165	/* scale_update_adj_time_ns now relects the configured delta, the
166	 * wrap_counter and the previous scaled frequency. Thus delta and
167	 * wrap_counter should be reset, and the scale frequency is updated
168	 * to the new frequency.
169	 */
170	data->delta = 0;
171	data->wrap_counter = 0;
172	data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm;
173	IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n",
174		      scaled_ppm, (unsigned long long)data->scaled_freq);
175	spin_unlock_bh(&data->lock);
176	return 0;
177}
178
179static void iwl_mld_ptp_work(struct work_struct *wk)
180{
181	struct iwl_mld *mld = container_of(wk, struct iwl_mld,
182					   ptp_data.dwork.work);
183	struct ptp_data *data = &mld->ptp_data;
184	u32 gp2;
185
186	spin_lock_bh(&data->lock);
187	if (!iwl_mld_get_systime(mld, &gp2))
188		iwl_mld_ptp_update_new_read(mld, gp2);
189	else
190		IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n");
191	spin_unlock_bh(&data->lock);
192}
193
194static int
195iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time)
196{
197	struct iwl_synced_time_cmd synced_time_cmd = {
198		.operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH)
199	};
200	struct iwl_host_cmd cmd = {
201		.id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD),
202		.flags = CMD_WANT_SKB,
203		.data[0] = &synced_time_cmd,
204		.len[0] = sizeof(synced_time_cmd),
205	};
206	struct iwl_synced_time_rsp *resp;
207	struct iwl_rx_packet *pkt;
208	int ret;
209	u64 gp2_10ns;
210
211	wiphy_lock(mld->wiphy);
212	ret = iwl_mld_send_cmd(mld, &cmd);
213	wiphy_unlock(mld->wiphy);
214	if (ret)
215		return ret;
216
217	pkt = cmd.resp_pkt;
218
219	if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) {
220		IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n");
221		iwl_free_resp(&cmd);
222		return -EIO;
223	}
224
225	resp = (void *)pkt->data;
226
227	gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 |
228		le32_to_cpu(resp->gp2_timestamp_lo);
229	*gp2 = div_u64(gp2_10ns, 100);
230
231	*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
232		le32_to_cpu(resp->platform_timestamp_lo);
233
234	iwl_free_resp(&cmd);
235	return ret;
236}
237
238static int
239iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
240			       struct system_device_crosststamp *xtstamp)
241{
242	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
243					   ptp_data.ptp_clock_info);
244	struct ptp_data *data = &mld->ptp_data;
245	int ret = 0;
246	/* Raw value read from GP2 register in usec */
247	u32 gp2;
248	/* GP2 value in ns*/
249	s64 gp2_ns;
250	/* System (wall) time */
251	ktime_t sys_time;
252
253	memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
254
255	ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time);
256	if (ret) {
257		IWL_DEBUG_PTP(mld,
258			      "PTP: fw get_crosstimestamp failed (ret=%d)\n",
259			      ret);
260		return ret;
261	}
262
263	spin_lock_bh(&data->lock);
264	gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
265	spin_unlock_bh(&data->lock);
266
267	IWL_DEBUG_PTP(mld,
268		      "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
269		      gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
270
271	/* System monotonic raw time is not used */
272	xtstamp->device = ns_to_ktime(gp2_ns);
273	xtstamp->sys_realtime = sys_time;
274
275	return ret;
276}
277
278void iwl_mld_ptp_init(struct iwl_mld *mld)
279{
280	if (WARN_ON(mld->ptp_data.ptp_clock))
281		return;
282
283	spin_lock_init(&mld->ptp_data.lock);
284	INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work);
285
286	mld->ptp_data.ptp_clock_info.owner = THIS_MODULE;
287	mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime;
288	mld->ptp_data.ptp_clock_info.settime64 = iwl_mld_ptp_settime;
289	mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
290	mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime;
291	mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine;
292	mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR;
293	mld->ptp_data.ptp_clock_info.getcrosststamp =
294					iwl_mld_phc_get_crosstimestamp;
295
296	/* Give a short 'friendly name' to identify the PHC clock */
297	snprintf(mld->ptp_data.ptp_clock_info.name,
298		 sizeof(mld->ptp_data.ptp_clock_info.name),
299		 "%s", "iwlwifi-PTP");
300
301	mld->ptp_data.ptp_clock =
302		ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
303
304	if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
305		IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
306			PTR_ERR(mld->ptp_data.ptp_clock));
307		mld->ptp_data.ptp_clock = NULL;
308	} else {
309		IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
310			       mld->ptp_data.ptp_clock_info.name,
311			       ptp_clock_index(mld->ptp_data.ptp_clock));
312	}
313}
314
315void iwl_mld_ptp_remove(struct iwl_mld *mld)
316{
317	if (mld->ptp_data.ptp_clock) {
318		IWL_DEBUG_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n",
319			       mld->ptp_data.ptp_clock_info.name,
320			       ptp_clock_index(mld->ptp_data.ptp_clock));
321
322		ptp_clock_unregister(mld->ptp_data.ptp_clock);
323		mld->ptp_data.ptp_clock = NULL;
324		mld->ptp_data.last_gp2 = 0;
325		mld->ptp_data.wrap_counter = 0;
326		cancel_delayed_work_sync(&mld->ptp_data.dwork);
327	}
328}
~/ Read-only file preview.