Merge tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-block.git] / drivers / net / wireless / intel / iwlwifi / mvm / ptp.c
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2021 - 2023 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
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
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
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
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
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
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
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
223 static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
224 {
225         struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
226                                            ptp_data.ptp_clock_info);
227         struct ptp_data *data = container_of(ptp, struct ptp_data,
228                                              ptp_clock_info);
229
230         mutex_lock(&mvm->mutex);
231         data->delta += delta;
232         IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta,
233                        (long long)data->delta);
234         mutex_unlock(&mvm->mutex);
235         return 0;
236 }
237
238 static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
239 {
240         struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm,
241                                            ptp_data.ptp_clock_info);
242         struct ptp_data *data = &mvm->ptp_data;
243         u32 gp2;
244
245         mutex_lock(&mvm->mutex);
246
247         /* Must call _iwl_mvm_ptp_get_adj_time() before updating
248          * data->scale_update_gp2 or data->scaled_freq since
249          * scale_update_adj_time_ns should reflect the previous scaled_freq.
250          */
251         gp2 = iwl_mvm_get_systime(mvm);
252         data->scale_update_adj_time_ns =
253                 iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC);
254         data->scale_update_gp2 = gp2;
255         data->wrap_counter = 0;
256         data->delta = 0;
257
258         data->scaled_freq = SCALE_FACTOR + scaled_ppm;
259         IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n",
260                        scaled_ppm, (unsigned long long)data->scaled_freq);
261
262         mutex_unlock(&mvm->mutex);
263         return 0;
264 }
265
266 /* iwl_mvm_ptp_init - initialize PTP for devices which support it.
267  * @mvm: internal mvm structure, see &struct iwl_mvm.
268  *
269  * Performs the required steps for enabling PTP support.
270  */
271 void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
272 {
273         /* Warn if the interface already has a ptp_clock defined */
274         if (WARN_ON(mvm->ptp_data.ptp_clock))
275                 return;
276
277         mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE;
278         mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff;
279         mvm->ptp_data.ptp_clock_info.getcrosststamp =
280                                         iwl_mvm_phc_get_crosstimestamp;
281         mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine;
282         mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime;
283         mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime;
284         mvm->ptp_data.scaled_freq = SCALE_FACTOR;
285
286         /* Give a short 'friendly name' to identify the PHC clock */
287         snprintf(mvm->ptp_data.ptp_clock_info.name,
288                  sizeof(mvm->ptp_data.ptp_clock_info.name),
289                  "%s", "iwlwifi-PTP");
290
291         INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work);
292
293         mvm->ptp_data.ptp_clock =
294                 ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev);
295
296         if (IS_ERR(mvm->ptp_data.ptp_clock)) {
297                 IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
298                         PTR_ERR(mvm->ptp_data.ptp_clock));
299                 mvm->ptp_data.ptp_clock = NULL;
300         } else if (mvm->ptp_data.ptp_clock) {
301                 IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
302                          mvm->ptp_data.ptp_clock_info.name,
303                          ptp_clock_index(mvm->ptp_data.ptp_clock));
304         }
305 }
306
307 /* iwl_mvm_ptp_remove - disable PTP device.
308  * @mvm: internal mvm structure, see &struct iwl_mvm.
309  *
310  * Disable PTP support.
311  */
312 void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
313 {
314         if (mvm->ptp_data.ptp_clock) {
315                 IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n",
316                          mvm->ptp_data.ptp_clock_info.name,
317                          ptp_clock_index(mvm->ptp_data.ptp_clock));
318
319                 ptp_clock_unregister(mvm->ptp_data.ptp_clock);
320                 mvm->ptp_data.ptp_clock = NULL;
321                 memset(&mvm->ptp_data.ptp_clock_info, 0,
322                        sizeof(mvm->ptp_data.ptp_clock_info));
323                 mvm->ptp_data.last_gp2 = 0;
324                 cancel_delayed_work_sync(&mvm->ptp_data.dwork);
325         }
326 }