1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2021 - 2023, 2025 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 return ret;
125 }
126
iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm * mvm,ktime_t * sys_time,u32 * gp2)127 static void iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm *mvm,
128 ktime_t *sys_time, u32 *gp2)
129 {
130 u64 diff = 0, new_diff;
131 u64 tmp_sys_time;
132 u32 tmp_gp2;
133 int i;
134
135 for (i = 0; i < IWL_PTP_GET_CROSS_TS_NUM; i++) {
136 iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &tmp_gp2, NULL,
137 &tmp_sys_time);
138 new_diff = tmp_sys_time - ((u64)tmp_gp2 * NSEC_PER_USEC);
139 if (!diff || new_diff < diff) {
140 *sys_time = tmp_sys_time;
141 *gp2 = tmp_gp2;
142 diff = new_diff;
143 IWL_DEBUG_INFO(mvm, "PTP: new times: gp2=%u sys=%lld\n",
144 *gp2, *sys_time);
145 }
146 }
147 }
148
149 static int
iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info * ptp,struct system_device_crosststamp * xtstamp)150 iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp,
151 struct system_device_crosststamp *xtstamp)
152 {
153 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
154 ptp_data.ptp_clock_info);
155 int ret = 0;
156 /* Raw value read from GP2 register in usec */
157 u32 gp2;
158 /* GP2 value in ns*/
159 s64 gp2_ns;
160 /* System (wall) time */
161 ktime_t sys_time;
162
163 memset(xtstamp, 0, sizeof(struct system_device_crosststamp));
164
165 if (!mvm->ptp_data.ptp_clock) {
166 IWL_ERR(mvm, "No PHC clock registered\n");
167 return -ENODEV;
168 }
169
170 mutex_lock(&mvm->mutex);
171 if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) {
172 ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time);
173
174 if (ret)
175 goto out;
176 } else {
177 iwl_mvm_phc_get_crosstimestamp_loop(mvm, &sys_time, &gp2);
178 }
179
180 gp2_ns = iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC);
181
182 IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n",
183 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time);
184
185 /* System monotonic raw time is not used */
186 xtstamp->device = (ktime_t)gp2_ns;
187 xtstamp->sys_realtime = sys_time;
188
189 out:
190 mutex_unlock(&mvm->mutex);
191 return ret;
192 }
193
iwl_mvm_ptp_work(struct work_struct * wk)194 static void iwl_mvm_ptp_work(struct work_struct *wk)
195 {
196 struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
197 ptp_data.dwork.work);
198 u32 gp2;
199
200 mutex_lock(&mvm->mutex);
201 gp2 = iwl_mvm_get_systime(mvm);
202 iwl_mvm_ptp_update_new_read(mvm, gp2);
203 mutex_unlock(&mvm->mutex);
204 }
205
iwl_mvm_ptp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)206 static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp,
207 struct timespec64 *ts)
208 {
209 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
210 ptp_data.ptp_clock_info);
211 u64 gp2;
212 u64 ns;
213
214 mutex_lock(&mvm->mutex);
215 gp2 = iwl_mvm_get_systime(mvm);
216 ns = iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
217 mutex_unlock(&mvm->mutex);
218
219 *ts = ns_to_timespec64(ns);
220 return 0;
221 }
222
iwl_mvm_ptp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)223 static int iwl_mvm_ptp_settime(struct ptp_clock_info *ptp,
224 const struct timespec64 *ts)
225 {
226 return -EOPNOTSUPP;
227 }
228
iwl_mvm_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)229 static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
230 {
231 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
232 ptp_data.ptp_clock_info);
233 struct ptp_data *data = container_of(ptp, struct ptp_data,
234 ptp_clock_info);
235
236 mutex_lock(&mvm->mutex);
237 data->delta += delta;
238 IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta,
239 (long long)data->delta);
240 mutex_unlock(&mvm->mutex);
241 return 0;
242 }
243
iwl_mvm_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)244 static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
245 {
246 struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
247 ptp_data.ptp_clock_info);
248 struct ptp_data *data = &mvm->ptp_data;
249 u32 gp2;
250
251 mutex_lock(&mvm->mutex);
252
253 /* Must call _iwl_mvm_ptp_get_adj_time() before updating
254 * data->scale_update_gp2 or data->scaled_freq since
255 * scale_update_adj_time_ns should reflect the previous scaled_freq.
256 */
257 gp2 = iwl_mvm_get_systime(mvm);
258 data->scale_update_adj_time_ns =
259 iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
260 data->scale_update_gp2 = gp2;
261 data->wrap_counter = 0;
262 data->delta = 0;
263
264 data->scaled_freq = SCALE_FACTOR + scaled_ppm;
265 IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n",
266 scaled_ppm, (unsigned long long)data->scaled_freq);
267
268 mutex_unlock(&mvm->mutex);
269 return 0;
270 }
271
272 /* iwl_mvm_ptp_init - initialize PTP for devices which support it.
273 * @mvm: internal mvm structure, see &struct iwl_mvm.
274 *
275 * Performs the required steps for enabling PTP support.
276 */
iwl_mvm_ptp_init(struct iwl_mvm * mvm)277 void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
278 {
279 /* Warn if the interface already has a ptp_clock defined */
280 if (WARN_ON(mvm->ptp_data.ptp_clock))
281 return;
282
283 mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
284 mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
285 mvm->ptp_data.ptp_clock_info.getcrosststamp =
286 iwl_mvm_phc_get_crosstimestamp;
287 mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine;
288 mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime;
289 mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime;
290 mvm->ptp_data.ptp_clock_info.settime64 = iwl_mvm_ptp_settime;
291 mvm->ptp_data.scaled_freq = SCALE_FACTOR;
292
293 /* Give a short 'friendly name' to identify the PHC clock */
294 snprintf(mvm->ptp_data.ptp_clock_info.name,
295 sizeof(mvm->ptp_data.ptp_clock_info.name),
296 "%s", "iwlwifi-PTP");
297
298 INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
299
300 mvm->ptp_data.ptp_clock =
301 ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
302
303 if (IS_ERR(mvm->ptp_data.ptp_clock)) {
304 IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
305 PTR_ERR(mvm->ptp_data.ptp_clock));
306 mvm->ptp_data.ptp_clock = NULL;
307 } else if (mvm->ptp_data.ptp_clock) {
308 IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
309 mvm->ptp_data.ptp_clock_info.name,
310 ptp_clock_index(mvm->ptp_data.ptp_clock));
311 }
312 }
313
314 /* iwl_mvm_ptp_remove - disable PTP device.
315 * @mvm: internal mvm structure, see &struct iwl_mvm.
316 *
317 * Disable PTP support.
318 */
iwl_mvm_ptp_remove(struct iwl_mvm * mvm)319 void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
320 {
321 if (mvm->ptp_data.ptp_clock) {
322 IWL_DEBUG_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
323 mvm->ptp_data.ptp_clock_info.name,
324 ptp_clock_index(mvm->ptp_data.ptp_clock));
325
326 ptp_clock_unregister(mvm->ptp_data.ptp_clock);
327 mvm->ptp_data.ptp_clock = NULL;
328 memset(&mvm->ptp_data.ptp_clock_info, 0,
329 sizeof(mvm->ptp_data.ptp_clock_info));
330 mvm->ptp_data.last_gp2 = 0;
331 cancel_delayed_work_sync(&mvm->ptp_data.dwork);
332 }
333 }
334