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 
iwl_mld_get_systime(struct iwl_mld * mld,u32 * gp2)22 static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2)
23 {
24 	*gp2 = iwl_read_prph(mld->trans, mld->trans->cfg->gp2_reg_addr);
25 
26 	if (*gp2 == 0x5a5a5a5a)
27 		return -EINVAL;
28 
29 	return 0;
30 }
31 
iwl_mld_ptp_update_new_read(struct iwl_mld * mld,u32 gp2)32 static 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 
iwl_mld_ptp_get_adj_time(struct iwl_mld * mld,u64 base_time_ns)59 u64 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 
iwl_mld_ptp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)102 static 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 
iwl_mld_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)124 static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
125 {
126 	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
127 					   ptp_data.ptp_clock_info);
128 	struct ptp_data *data = &mld->ptp_data;
129 
130 	spin_lock_bh(&data->lock);
131 	data->delta += delta;
132 	IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta,
133 		      (long long)data->delta);
134 	spin_unlock_bh(&data->lock);
135 	return 0;
136 }
137 
iwl_mld_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)138 static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
139 {
140 	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
141 					   ptp_data.ptp_clock_info);
142 	struct ptp_data *data = &mld->ptp_data;
143 	u32 gp2;
144 
145 	/* Must call iwl_mld_ptp_get_adj_time() before updating
146 	 * data->scale_update_gp2 or data->scaled_freq since
147 	 * scale_update_adj_time_ns should reflect the previous scaled_freq.
148 	 */
149 	if (iwl_mld_get_systime(mld, &gp2)) {
150 		IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n");
151 		return -EBUSY;
152 	}
153 
154 	spin_lock_bh(&data->lock);
155 	data->scale_update_adj_time_ns =
156 		iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC);
157 	data->scale_update_gp2 = gp2;
158 
159 	/* scale_update_adj_time_ns now relects the configured delta, the
160 	 * wrap_counter and the previous scaled frequency. Thus delta and
161 	 * wrap_counter should be reset, and the scale frequency is updated
162 	 * to the new frequency.
163 	 */
164 	data->delta = 0;
165 	data->wrap_counter = 0;
166 	data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm;
167 	IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n",
168 		      scaled_ppm, (unsigned long long)data->scaled_freq);
169 	spin_unlock_bh(&data->lock);
170 	return 0;
171 }
172 
iwl_mld_ptp_work(struct work_struct * wk)173 static void iwl_mld_ptp_work(struct work_struct *wk)
174 {
175 	struct iwl_mld *mld = container_of(wk, struct iwl_mld,
176 					   ptp_data.dwork.work);
177 	struct ptp_data *data = &mld->ptp_data;
178 	u32 gp2;
179 
180 	spin_lock_bh(&data->lock);
181 	if (!iwl_mld_get_systime(mld, &gp2))
182 		iwl_mld_ptp_update_new_read(mld, gp2);
183 	else
184 		IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n");
185 	spin_unlock_bh(&data->lock);
186 }
187 
188 static int
iwl_mld_get_crosstimestamp_fw(struct iwl_mld * mld,u32 * gp2,u64 * sys_time)189 iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time)
190 {
191 	struct iwl_synced_time_cmd synced_time_cmd = {
192 		.operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH)
193 	};
194 	struct iwl_host_cmd cmd = {
195 		.id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD),
196 		.flags = CMD_WANT_SKB,
197 		.data[0] = &synced_time_cmd,
198 		.len[0] = sizeof(synced_time_cmd),
199 	};
200 	struct iwl_synced_time_rsp *resp;
201 	struct iwl_rx_packet *pkt;
202 	int ret;
203 	u64 gp2_10ns;
204 
205 	wiphy_lock(mld->wiphy);
206 	ret = iwl_mld_send_cmd(mld, &cmd);
207 	wiphy_unlock(mld->wiphy);
208 	if (ret)
209 		return ret;
210 
211 	pkt = cmd.resp_pkt;
212 
213 	if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) {
214 		IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n");
215 		iwl_free_resp(&cmd);
216 		return -EIO;
217 	}
218 
219 	resp = (void *)pkt->data;
220 
221 	gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 |
222 		le32_to_cpu(resp->gp2_timestamp_lo);
223 	*gp2 = div_u64(gp2_10ns, 100);
224 
225 	*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 |
226 		le32_to_cpu(resp->platform_timestamp_lo);
227 
228 	iwl_free_resp(&cmd);
229 	return ret;
230 }
231 
232 static int
iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)233 iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
234 			       struct system_device_crosststamp *xtstamp)
235 {
236 	struct iwl_mld *mld = container_of(ptp, struct iwl_mld,
237 					   ptp_data.ptp_clock_info);
238 	struct ptp_data *data = &mld->ptp_data;
239 	int ret = 0;
240 	/* Raw value read from GP2 register in usec */
241 	u32 gp2;
242 	/* GP2 value in ns*/
243 	s64 gp2_ns;
244 	/* System (wall) time */
245 	ktime_t sys_time;
246 
247 	memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
248 
249 	ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time);
250 	if (ret) {
251 		IWL_DEBUG_PTP(mld,
252 			      "PTP: fw get_crosstimestamp failed (ret=%d)\n",
253 			      ret);
254 		return ret;
255 	}
256 
257 	spin_lock_bh(&data->lock);
258 	gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC);
259 	spin_unlock_bh(&data->lock);
260 
261 	IWL_DEBUG_PTP(mld,
262 		      "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
263 		      gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
264 
265 	/* System monotonic raw time is not used */
266 	xtstamp->device = ns_to_ktime(gp2_ns);
267 	xtstamp->sys_realtime = sys_time;
268 
269 	return ret;
270 }
271 
iwl_mld_ptp_init(struct iwl_mld * mld)272 void iwl_mld_ptp_init(struct iwl_mld *mld)
273 {
274 	if (WARN_ON(mld->ptp_data.ptp_clock))
275 		return;
276 
277 	spin_lock_init(&mld->ptp_data.lock);
278 	INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work);
279 
280 	mld->ptp_data.ptp_clock_info.owner = THIS_MODULE;
281 	mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime;
282 	mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
283 	mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime;
284 	mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine;
285 	mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR;
286 	mld->ptp_data.ptp_clock_info.getcrosststamp =
287 					iwl_mld_phc_get_crosstimestamp;
288 
289 	/* Give a short 'friendly name' to identify the PHC clock */
290 	snprintf(mld->ptp_data.ptp_clock_info.name,
291 		 sizeof(mld->ptp_data.ptp_clock_info.name),
292 		 "%s", "iwlwifi-PTP");
293 
294 	mld->ptp_data.ptp_clock =
295 		ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
296 
297 	if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
298 		IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
299 			PTR_ERR(mld->ptp_data.ptp_clock));
300 		mld->ptp_data.ptp_clock = NULL;
301 	} else {
302 		IWL_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
303 			 mld->ptp_data.ptp_clock_info.name,
304 			 ptp_clock_index(mld->ptp_data.ptp_clock));
305 	}
306 }
307 
iwl_mld_ptp_remove(struct iwl_mld * mld)308 void iwl_mld_ptp_remove(struct iwl_mld *mld)
309 {
310 	if (mld->ptp_data.ptp_clock) {
311 		IWL_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n",
312 			 mld->ptp_data.ptp_clock_info.name,
313 			 ptp_clock_index(mld->ptp_data.ptp_clock));
314 
315 		ptp_clock_unregister(mld->ptp_data.ptp_clock);
316 		mld->ptp_data.ptp_clock = NULL;
317 		mld->ptp_data.last_gp2 = 0;
318 		mld->ptp_data.wrap_counter = 0;
319 		cancel_delayed_work_sync(&mld->ptp_data.dwork);
320 	}
321 }
322