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