1 /*
2 * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: wlan_hdd_tsf.c - WLAN Host Device Driver tsf related implementation
22 */
23
24 #include "osif_sync.h"
25 #include "wlan_hdd_main.h"
26 #include "wlan_hdd_tsf.h"
27 #include "wma_api.h"
28 #include "wlan_fwol_ucfg_api.h"
29 #include <qca_vendor.h>
30 #include <linux/errqueue.h>
31 #if defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ) || \
32 defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) || \
33 defined(WLAN_FEATURE_TSF_ACCURACY)
34 #include <linux/gpio.h>
35 #endif
36
37 #include "ol_txrx_api.h"
38 #ifdef WLAN_FEATURE_TSF_AUTO_REPORT
39 #include <cdp_txrx_ctrl.h>
40 #endif
41
42 #ifdef WLAN_FEATURE_TSF_PLUS
43 #if !defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) && \
44 !defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) && \
45 !defined(WLAN_FEATURE_TSF_TIMER_SYNC)
46 static int tsf_gpio_irq_num = -1;
47 #endif
48 #endif
49 static qdf_event_t tsf_sync_get_completion_evt;
50 #define WLAN_TSF_SYNC_GET_TIMEOUT 2000
51 #define WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS 500
52 #define WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS 100
53 #ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC
54 #define WLAN_HDD_SOFTAP_INTERVAL_TIMES 1
55 #else
56 #define WLAN_HDD_SOFTAP_INTERVAL_TIMES 100
57 #endif
58 #define OUTPUT_HIGH 1
59 #define OUTPUT_LOW 0
60
61 #ifdef WLAN_FEATURE_TSF_PLUS
62 #if defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) || \
63 defined(WLAN_FEATURE_TSF_TIMER_SYNC)
64 static void hdd_update_timestamp(struct hdd_adapter *adapter);
65 #else
66 static void
67 hdd_update_timestamp(struct hdd_adapter *adapter,
68 uint64_t target_time, uint64_t host_time);
69 #endif
70 #endif
71
72 #ifdef QCA_GET_TSF_VIA_REG
73 static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf);
74
75 /**
76 * struct hdd_tsf_report - TSF report filled in by DP layer
77 * @vdev_id: vdev id for which TSF values is to be read
78 * @tsf_id: tsf if used to read TSF report
79 * @mac_id: lmac_id for which TSF values are read
80 * @tsf: 64 bit tsf value as read from scratch registers
81 * @tsf_sync_soc_time: host qtimer time when scratch registers are read
82 *
83 * The structure is used by the upper layers to pass vdev_id, tsf_id and mac_id
84 * information to DP layer and get tsf time and host time when TSF was read.
85 */
86 struct hdd_tsf_report {
87 uint32_t vdev_id;
88 uint32_t tsf_id;
89 uint32_t mac_id;
90 uint64_t tsf;
91 uint64_t tsf_sync_soc_time;
92 };
93 #endif
94
95 /**
96 * enum hdd_tsf_op_result - result of tsf operation
97 * @HDD_TSF_OP_SUCC: succeed
98 * @HDD_TSF_OP_FAIL: fail
99 */
100 enum hdd_tsf_op_result {
101 HDD_TSF_OP_SUCC,
102 HDD_TSF_OP_FAIL
103 };
104
105 #ifdef WLAN_FEATURE_TSF_PLUS
106 #ifdef WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC
107 #define WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL 1
108 #else
109 #define WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL 9
110 #endif
hdd_set_th_sync_status(struct hdd_adapter * adapter,bool initialized)111 static inline void hdd_set_th_sync_status(struct hdd_adapter *adapter,
112 bool initialized)
113 {
114 qdf_atomic_set(&adapter->tsf.tsf_sync_ready_flag,
115 (initialized ? 1 : 0));
116 }
117
hdd_get_th_sync_status(struct hdd_adapter * adapter)118 static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter)
119 {
120 return qdf_atomic_read(&adapter->tsf.tsf_sync_ready_flag) != 0;
121 }
122
123 #else
hdd_get_th_sync_status(struct hdd_adapter * adapter)124 static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter)
125 {
126 return true;
127 }
128 #endif
129
130 static
hdd_tsf_check_conn_state(struct hdd_adapter * adapter)131 enum hdd_tsf_get_state hdd_tsf_check_conn_state(struct hdd_adapter *adapter)
132 {
133 enum QDF_OPMODE mode;
134 enum hdd_tsf_get_state ret = TSF_RETURN;
135
136 mode = adapter->device_mode;
137
138 if (!test_bit(SOFTAP_BSS_STARTED, &adapter->deflink->link_flags) &&
139 (mode == QDF_SAP_MODE || mode == QDF_P2P_GO_MODE)) {
140 hdd_err("Soft AP / P2p GO not beaconing");
141 ret = TSF_SAP_NOT_STARTED_NO_TSF;
142 } else if (!hdd_cm_is_vdev_associated(adapter->deflink) &&
143 (mode == QDF_STA_MODE || mode == QDF_P2P_CLIENT_MODE)) {
144 hdd_err("failed to cap tsf, not connect with ap");
145 ret = TSF_STA_NOT_CONNECTED_NO_TSF;
146 }
147
148 return ret;
149 }
150
hdd_tsf_is_initialized(struct hdd_adapter * adapter)151 static bool hdd_tsf_is_initialized(struct hdd_adapter *adapter)
152 {
153 struct hdd_context *hddctx;
154
155 if (!adapter) {
156 hdd_err("invalid adapter");
157 return false;
158 }
159
160 hddctx = WLAN_HDD_GET_CTX(adapter);
161 if (!hddctx) {
162 hdd_err("invalid hdd context");
163 return false;
164 }
165
166 if (!qdf_atomic_read(&hddctx->tsf.tsf_ready_flag) ||
167 !hdd_get_th_sync_status(adapter)) {
168 hdd_err("TSF is not initialized");
169 return false;
170 }
171
172 return true;
173 }
174
175 #if (defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) && \
176 defined(WLAN_FEATURE_TSF_PLUS)) || \
177 defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) || \
178 defined(WLAN_FEATURE_TSF_TIMER_SYNC)
179 /**
180 * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync
181 * @adapter: pointer to adapter
182 *
183 * This function send WMI command to reset GPIO configured in FW after
184 * TSF get operation.
185 *
186 * Return: TSF_RETURN on Success, TSF_RESET_GPIO_FAIL on failure
187 */
hdd_tsf_reset_gpio(struct hdd_adapter * adapter)188 static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter)
189 {
190 /* No GPIO Host timer sync for integrated WIFI Device */
191 return TSF_RETURN;
192 }
193
194 /**
195 * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync
196 * @hdd_ctx: pointer to hdd context
197 *
198 * This function is a dummy function for adrastea arch
199 *
200 * Return: QDF_STATUS_SUCCESS on Success
201 */
202
hdd_tsf_set_gpio(struct hdd_context * hdd_ctx)203 static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx)
204 {
205 return QDF_STATUS_SUCCESS;
206 }
207 #else
hdd_tsf_reset_gpio(struct hdd_adapter * adapter)208 static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter)
209 {
210 int ret;
211
212 ret = wma_cli_set_command((int)adapter->deflink->vdev_id,
213 (int)GEN_PARAM_RESET_TSF_GPIO,
214 adapter->deflink->vdev_id,
215 GEN_CMD);
216
217 if (ret != 0) {
218 hdd_err("tsf reset GPIO fail ");
219 ret = TSF_RESET_GPIO_FAIL;
220 } else {
221 ret = TSF_RETURN;
222 }
223 return ret;
224 }
225
226 /**
227 * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync
228 * @hdd_ctx: pointer to hdd context
229 *
230 * This function check GPIO and set GPIO as IRQ to FW side on
231 * none Adrastea arch
232 *
233 * Return: QDF_STATUS_SUCCESS on Success, others on Failure.
234 */
hdd_tsf_set_gpio(struct hdd_context * hdd_ctx)235 static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx)
236 {
237 QDF_STATUS status;
238 uint32_t tsf_gpio_pin = TSF_GPIO_PIN_INVALID;
239
240 status = ucfg_fwol_get_tsf_gpio_pin(hdd_ctx->psoc, &tsf_gpio_pin);
241 if (QDF_IS_STATUS_ERROR(status))
242 return QDF_STATUS_E_INVAL;
243
244 if (tsf_gpio_pin == TSF_GPIO_PIN_INVALID)
245 return QDF_STATUS_E_INVAL;
246
247 status = sme_set_tsf_gpio(hdd_ctx->mac_handle,
248 tsf_gpio_pin);
249
250 return status;
251 }
252 #endif
253
254 #ifdef WLAN_FEATURE_TSF_PLUS
hdd_tsf_is_ptp_enabled(struct hdd_context * hdd)255 static bool hdd_tsf_is_ptp_enabled(struct hdd_context *hdd)
256 {
257 uint32_t tsf_ptp_options;
258
259 if (hdd && QDF_IS_STATUS_SUCCESS(
260 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
261 return !!tsf_ptp_options;
262 else
263 return false;
264 }
265
hdd_tsf_is_tx_set(struct hdd_context * hdd)266 bool hdd_tsf_is_tx_set(struct hdd_context *hdd)
267 {
268 uint32_t tsf_ptp_options;
269
270 if (hdd && QDF_IS_STATUS_SUCCESS(
271 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
272 return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_TX;
273 else
274 return false;
275 }
276
hdd_tsf_is_rx_set(struct hdd_context * hdd)277 bool hdd_tsf_is_rx_set(struct hdd_context *hdd)
278 {
279 uint32_t tsf_ptp_options;
280
281 if (hdd && QDF_IS_STATUS_SUCCESS(
282 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
283 return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_RX;
284 else
285 return false;
286 }
287
hdd_tsf_is_raw_set(struct hdd_context * hdd)288 bool hdd_tsf_is_raw_set(struct hdd_context *hdd)
289 {
290 uint32_t tsf_ptp_options;
291
292 if (hdd && QDF_IS_STATUS_SUCCESS(
293 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
294 return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_RAW;
295 else
296 return false;
297 }
298
hdd_tsf_is_dbg_fs_set(struct hdd_context * hdd)299 bool hdd_tsf_is_dbg_fs_set(struct hdd_context *hdd)
300 {
301 uint32_t tsf_ptp_options;
302
303 if (hdd && QDF_IS_STATUS_SUCCESS(
304 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
305 return tsf_ptp_options & CFG_SET_TSF_DBG_FS;
306 else
307 return false;
308 }
309
hdd_tsf_is_tsf64_tx_set(struct hdd_context * hdd)310 bool hdd_tsf_is_tsf64_tx_set(struct hdd_context *hdd)
311 {
312 uint32_t tsf_ptp_options;
313
314 if (hdd && QDF_IS_STATUS_SUCCESS(
315 ucfg_fwol_get_tsf_ptp_options(hdd->psoc, &tsf_ptp_options)))
316 return tsf_ptp_options & CFG_SET_TSF_PTP_OPT_TSF64_TX;
317 else
318 return false;
319 }
320
hdd_tsf_is_time_sync_enabled_cfg(struct hdd_context * hdd_ctx)321 bool hdd_tsf_is_time_sync_enabled_cfg(struct hdd_context *hdd_ctx)
322 {
323 uint32_t tsf_ptp_options;
324
325 if (hdd_ctx && QDF_IS_STATUS_SUCCESS(
326 ucfg_fwol_get_tsf_ptp_options(hdd_ctx->psoc, &tsf_ptp_options)))
327 return tsf_ptp_options & CFG_SET_TSF_PTP_SYNC_PERIOD;
328 else
329 return false;
330 }
331
hdd_is_tsf_sync_enabled(struct hdd_context * hdd)332 static bool hdd_is_tsf_sync_enabled(struct hdd_context *hdd)
333 {
334 bool is_tsf_sync_enable;
335
336 if (hdd && QDF_IS_STATUS_SUCCESS(
337 ucfg_fwol_get_tsf_sync_enable(hdd->psoc, &is_tsf_sync_enable)))
338 return is_tsf_sync_enable;
339 else
340 return false;
341 }
342
hdd_update_dynamic_tsf_sync(struct hdd_adapter * adapter)343 void hdd_update_dynamic_tsf_sync(struct hdd_adapter *adapter)
344 {
345 adapter->tsf.enable_dynamic_tsf_sync =
346 hdd_is_tsf_sync_enabled(adapter->hdd_ctx);
347 }
348 #else
349
hdd_tsf_is_ptp_enabled(struct hdd_context * hdd)350 static bool hdd_tsf_is_ptp_enabled(struct hdd_context *hdd)
351 {
352 return false;
353 }
354 #endif
355
356 #ifdef WLAN_FEATURE_TSF_PLUS
357 static inline
hdd_get_monotonic_host_time(struct hdd_context * hdd_ctx)358 uint64_t hdd_get_monotonic_host_time(struct hdd_context *hdd_ctx)
359 {
360 return hdd_tsf_is_raw_set(hdd_ctx) ?
361 ktime_get_ns() : ktime_get_real_ns();
362 }
363 #endif
364
365 #if defined(WLAN_FEATURE_TSF_PLUS) && \
366 defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC)
367 #define MAX_CONTINUOUS_RETRY_CNT 10
368 static uint32_t
hdd_wlan_retry_tsf_cap(struct hdd_adapter * adapter)369 hdd_wlan_retry_tsf_cap(struct hdd_adapter *adapter)
370 {
371 struct hdd_context *hddctx;
372 int count = adapter->tsf.continuous_cap_retry_count;
373
374 hddctx = WLAN_HDD_GET_CTX(adapter);
375 if (count == MAX_CONTINUOUS_RETRY_CNT) {
376 hdd_debug("Max retry count reached");
377 return 0;
378 }
379 qdf_atomic_set(&hddctx->tsf.cap_tsf_flag, 0);
380 count++;
381 adapter->tsf.continuous_cap_retry_count = count;
382 return (count * WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS);
383 }
384
385 static void
hdd_wlan_restart_tsf_cap(struct hdd_adapter * adapter)386 hdd_wlan_restart_tsf_cap(struct hdd_adapter *adapter)
387 {
388 struct hdd_context *hddctx;
389 int count = adapter->tsf.continuous_cap_retry_count;
390
391 hddctx = WLAN_HDD_GET_CTX(adapter);
392 if (count == MAX_CONTINUOUS_RETRY_CNT) {
393 hdd_debug("Restart TSF CAP");
394 qdf_atomic_set(&hddctx->tsf.cap_tsf_flag, 0);
395 adapter->tsf.continuous_cap_retry_count = 0;
396 qdf_mc_timer_start(&adapter->tsf.host_target_sync_timer,
397 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS);
398 }
399 }
400
401 static void
hdd_update_host_time(struct hdd_adapter * adapter)402 hdd_update_host_time(struct hdd_adapter *adapter)
403 {
404 struct hdd_context *hdd_ctx;
405 u64 host_time;
406 char *name = NULL;
407
408 hdd_ctx = adapter->hdd_ctx;
409
410 if (!hdd_tsf_is_initialized(adapter)) {
411 hdd_err("tsf is not init, exit");
412 return;
413 }
414
415 host_time = hdd_get_monotonic_host_time(hdd_ctx);
416 hdd_update_timestamp(adapter, 0, host_time);
417 name = adapter->dev->name;
418
419 hdd_debug("iface: %s - host_time: %llu",
420 (!name ? "none" : name), host_time);
421 }
422
423 static
hdd_tsf_ext_gpio_sync_work(void * data)424 void hdd_tsf_ext_gpio_sync_work(void *data)
425 {
426 QDF_STATUS status;
427 struct hdd_adapter *adapter;
428 struct hdd_context *hdd_ctx;
429 uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID;
430
431 adapter = data;
432 hdd_ctx = adapter->hdd_ctx;
433 status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc,
434 &tsf_sync_gpio_pin);
435 if (QDF_IS_STATUS_ERROR(status)) {
436 hdd_err("tsf sync gpio host pin error");
437 return;
438 }
439 gpio_set_value(tsf_sync_gpio_pin, OUTPUT_HIGH);
440 hdd_update_host_time(adapter);
441 usleep_range(50, 100);
442 gpio_set_value(tsf_sync_gpio_pin, OUTPUT_LOW);
443
444 status = wma_cli_set_command((int)adapter->deflink->vdev_id,
445 (int)GEN_PARAM_CAPTURE_TSF,
446 adapter->deflink->vdev_id, GEN_CMD);
447 if (status != QDF_STATUS_SUCCESS) {
448 hdd_err("cap tsf fail");
449 qdf_mc_timer_stop(&adapter->tsf.host_capture_req_timer);
450 }
451 }
452
453 static void
hdd_tsf_gpio_sync_work_init(struct hdd_adapter * adapter)454 hdd_tsf_gpio_sync_work_init(struct hdd_adapter *adapter)
455 {
456 qdf_create_work(0, &adapter->tsf.gpio_tsf_sync_work,
457 hdd_tsf_ext_gpio_sync_work, adapter);
458 }
459
460 static void
hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter * adapter)461 hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter *adapter)
462 {
463 qdf_destroy_work(0, &adapter->tsf.gpio_tsf_sync_work);
464 }
465
466 static void
hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter * adapter)467 hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter *adapter)
468 {
469 qdf_cancel_work(&adapter->tsf.gpio_tsf_sync_work);
470 }
471
472 static void
hdd_tsf_start_ext_gpio_sync(struct hdd_adapter * adapter)473 hdd_tsf_start_ext_gpio_sync(struct hdd_adapter *adapter)
474 {
475 qdf_sched_work(0, &adapter->tsf.gpio_tsf_sync_work);
476 }
477
hdd_tsf_cap_sync_send(struct hdd_adapter * adapter)478 static bool hdd_tsf_cap_sync_send(struct hdd_adapter *adapter)
479 {
480 hdd_tsf_start_ext_gpio_sync(adapter);
481 return true;
482 }
483 #elif defined(WLAN_FEATURE_TSF_PLUS) && \
484 !defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC)
485 static void
hdd_wlan_restart_tsf_cap(struct hdd_adapter * adapter)486 hdd_wlan_restart_tsf_cap(struct hdd_adapter *adapter)
487 {
488 }
489
490 static void
hdd_tsf_gpio_sync_work_init(struct hdd_adapter * adapter)491 hdd_tsf_gpio_sync_work_init(struct hdd_adapter *adapter)
492 {
493 }
494
495 static void
hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter * adapter)496 hdd_tsf_gpio_sync_work_deinit(struct hdd_adapter *adapter)
497 {
498 }
499
500 static void
hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter * adapter)501 hdd_tsf_stop_ext_gpio_sync(struct hdd_adapter *adapter)
502 {
503 }
504
505 static void
hdd_tsf_start_ext_gpio_sync(struct hdd_adapter * adapter)506 hdd_tsf_start_ext_gpio_sync(struct hdd_adapter *adapter)
507 {
508 }
509
510 static bool
hdd_tsf_cap_sync_send(struct hdd_adapter * adapter)511 hdd_tsf_cap_sync_send(struct hdd_adapter *adapter)
512 {
513 hdd_tsf_start_ext_gpio_sync(adapter);
514 return false;
515 }
516
517 #else
hdd_tsf_cap_sync_send(struct hdd_adapter * adapter)518 static bool hdd_tsf_cap_sync_send(struct hdd_adapter *adapter)
519 {
520 return false;
521 }
522 #endif
523
524 #ifdef WLAN_FEATURE_TSF_TIMER_SYNC
525 /**
526 * hdd_convert_qtime_to_us() - convert qtime to us
527 * @time: QTIMER ticks for adrastea and us for Lithium
528 *
529 * This function converts qtime to us.
530 *
531 * Return: Time in microseconds
532 */
533 static inline uint64_t
hdd_convert_qtime_to_us(uint64_t time)534 hdd_convert_qtime_to_us(uint64_t time)
535 {
536 return time;
537 }
538
539 #else
540 static inline uint64_t
hdd_convert_qtime_to_us(uint64_t time)541 hdd_convert_qtime_to_us(uint64_t time)
542 {
543 return qdf_log_timestamp_to_usecs(time);
544 }
545 #endif
546
547 /**
548 * hdd_capture_tsf_internal_via_wmi() - convert qtime to us
549 * @adapter: pointer to adapter
550 * @buf: in case of failure update with fail
551 * @len: buffer length
552 *
553 * Return: result of tsf operation
554 */
555 static enum hdd_tsf_op_result
hdd_capture_tsf_internal_via_wmi(struct hdd_adapter * adapter,uint32_t * buf,int len)556 hdd_capture_tsf_internal_via_wmi(struct hdd_adapter *adapter, uint32_t *buf,
557 int len)
558 {
559 int ret;
560 struct hdd_context *hddctx = adapter->hdd_ctx;
561
562 ret = wma_cli_set_command((int)adapter->deflink->vdev_id,
563 (int)GEN_PARAM_CAPTURE_TSF,
564 adapter->deflink->vdev_id, GEN_CMD);
565 if (ret != QDF_STATUS_SUCCESS) {
566 hdd_err("cap tsf fail");
567 buf[0] = TSF_CAPTURE_FAIL;
568 hddctx->tsf.cap_tsf_context = NULL;
569 qdf_atomic_set(&hddctx->tsf.cap_tsf_flag, 0);
570 qdf_mc_timer_stop(&adapter->tsf.host_capture_req_timer);
571 }
572 return HDD_TSF_OP_SUCC;
573 }
574
575 #ifndef QCA_GET_TSF_VIA_REG
576 static inline
_hdd_capture_tsf_internal(struct hdd_adapter * adapter,uint32_t * buf,int len)577 enum hdd_tsf_op_result _hdd_capture_tsf_internal(struct hdd_adapter *adapter,
578 uint32_t *buf, int len)
579 {
580 return hdd_capture_tsf_internal_via_wmi(adapter, buf, len);
581 }
582
wlan_hdd_tsf_reg_update_details(struct hdd_adapter * adapter,struct stsf * ptsf)583 static inline void wlan_hdd_tsf_reg_update_details(struct hdd_adapter *adapter,
584 struct stsf *ptsf)
585 {
586 }
587 #else
588
hdd_tsf_reg_is_details_valid(struct hdd_adapter * adapter)589 static inline int hdd_tsf_reg_is_details_valid(struct hdd_adapter *adapter)
590 {
591 return qdf_atomic_read(&adapter->tsf.tsf_details_valid);
592 }
593
594 static inline void
wlan_hdd_tsf_reg_update_details(struct hdd_adapter * adapter,struct stsf * ptsf)595 wlan_hdd_tsf_reg_update_details(struct hdd_adapter *adapter, struct stsf *ptsf)
596 {
597 if (ptsf->tsf_id_valid) {
598 adapter->tsf.tsf_id = ptsf->tsf_id;
599 adapter->tsf.tsf_mac_id = ptsf->mac_id;
600 qdf_atomic_set(&adapter->tsf.tsf_details_valid, 1);
601 }
602 hdd_debug("vdev_id %u tsf_id %u tsf_id_valid %u mac_id %u",
603 adapter->deflink->vdev_id, ptsf->tsf_id, ptsf->tsf_id_valid,
604 ptsf->mac_id);
605 }
606
607 static inline
wlan_hdd_tsf_reg_get(struct hdd_adapter * adapter,struct hdd_tsf_report * tsf_report)608 QDF_STATUS wlan_hdd_tsf_reg_get(struct hdd_adapter *adapter,
609 struct hdd_tsf_report *tsf_report)
610 {
611 ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC);
612 uint64_t tsf_time = 0;
613 uint64_t tsf_sync_soc_time = 0;
614
615 if (qdf_unlikely(!soc))
616 return QDF_STATUS_E_INVAL;
617
618 cdp_get_tsf_time(soc, tsf_report->tsf_id, tsf_report->mac_id,
619 &tsf_time, &tsf_sync_soc_time);
620
621 /* fill in the report */
622 tsf_report->tsf = tsf_time;
623 tsf_report->tsf_sync_soc_time = tsf_sync_soc_time;
624 return QDF_STATUS_SUCCESS;
625 }
626
627 /**
628 * wlan_hdd_tsf_reg_process_report() - Process tsf report
629 * @adapter: pointer to the adapter
630 * @tsf_report: pointer to tsf report
631 *
632 * This function process the tsf report received and update tsf
633 * value received via scratch register read to adapter
634 *
635 * Return: 0 for success or 1 in case of failure
636 */
637 static enum hdd_tsf_op_result
wlan_hdd_tsf_reg_process_report(struct hdd_adapter * adapter,struct hdd_tsf_report * tsf_report)638 wlan_hdd_tsf_reg_process_report(struct hdd_adapter *adapter,
639 struct hdd_tsf_report *tsf_report)
640 {
641 struct hdd_vdev_tsf *tsf;
642 QDF_TIMER_STATE capture_req_timer_status;
643 qdf_mc_timer_t *capture_timer;
644
645 if (!tsf_report->tsf && !tsf_report->tsf_sync_soc_time) {
646 hdd_err("Invalid TSF report");
647 return HDD_TSF_OP_FAIL;
648 }
649
650 if (!hdd_tsf_is_initialized(adapter)) {
651 hdd_err("tsf is not init, ignore tsf event");
652 return HDD_TSF_OP_FAIL;
653 }
654
655 hdd_debug("device_mode is %d", adapter->device_mode);
656
657 tsf = &adapter->tsf;
658 capture_timer = &tsf->host_capture_req_timer;
659 capture_req_timer_status =
660 qdf_mc_timer_get_current_state(capture_timer);
661 if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) {
662 hdd_warn("invalid timer status");
663 return HDD_TSF_OP_FAIL;
664 }
665
666 qdf_mc_timer_stop(capture_timer);
667 tsf->cur_target_time = tsf_report->tsf;
668 tsf->cur_tsf_sync_soc_time = tsf_report->tsf_sync_soc_time *
669 NSEC_PER_USEC;
670
671 qdf_event_set(&tsf_sync_get_completion_evt);
672 hdd_update_tsf(adapter, tsf->cur_target_time);
673 hdd_debug("vdev id=%u, tsf=%llu", adapter->deflink->vdev_id,
674 tsf_report->tsf);
675 return HDD_TSF_OP_SUCC;
676 }
677
678 static enum hdd_tsf_op_result
hdd_capture_tsf_internal_via_reg(struct hdd_adapter * adapter,uint32_t * buf,int len)679 hdd_capture_tsf_internal_via_reg(struct hdd_adapter *adapter, uint32_t *buf,
680 int len)
681 {
682 struct hdd_tsf_report tsf_report;
683
684 if (!hdd_tsf_reg_is_details_valid(adapter)) {
685 hdd_warn("TSF reg details are not valid!");
686 return HDD_TSF_OP_FAIL;
687 }
688
689 qdf_mem_zero(&tsf_report, sizeof(tsf_report));
690 tsf_report.vdev_id = adapter->deflink->vdev_id;
691 tsf_report.tsf_id = adapter->tsf.tsf_id;
692 tsf_report.mac_id = adapter->tsf.tsf_mac_id;
693
694 if (wlan_hdd_tsf_reg_get(adapter, &tsf_report)) {
695 hdd_warn("Unable to get tsf report");
696 return HDD_TSF_OP_FAIL;
697 }
698
699 return wlan_hdd_tsf_reg_process_report(adapter, &tsf_report);
700 }
701
702 static inline
_hdd_capture_tsf_internal(struct hdd_adapter * adapter,uint32_t * buf,int len)703 enum hdd_tsf_op_result _hdd_capture_tsf_internal(struct hdd_adapter *adapter,
704 uint32_t *buf, int len)
705 {
706 if (!qdf_atomic_read(&adapter->tsf.tsf_details_valid))
707 return hdd_capture_tsf_internal_via_wmi(adapter, buf, len);
708 else
709 return hdd_capture_tsf_internal_via_reg(adapter, buf, len);
710 }
711
712 #endif /* QCA_GET_TSF_VIA_REG */
713
hdd_capture_tsf_internal(struct hdd_adapter * adapter,uint32_t * buf,int len)714 static enum hdd_tsf_op_result hdd_capture_tsf_internal(
715 struct hdd_adapter *adapter, uint32_t *buf, int len)
716 {
717 enum hdd_tsf_op_result ret;
718 struct hdd_context *hddctx;
719 qdf_mc_timer_t *cap_timer;
720
721 if (!adapter || !buf) {
722 hdd_err("invalid pointer");
723 return HDD_TSF_OP_FAIL;
724 }
725
726 if (len != 1)
727 return HDD_TSF_OP_FAIL;
728
729 hddctx = WLAN_HDD_GET_CTX(adapter);
730 if (!hddctx) {
731 hdd_err("invalid hdd context");
732 return HDD_TSF_OP_FAIL;
733 }
734
735 if (wlan_hdd_validate_context(hddctx)) {
736 hdd_err("hdd context validation failed");
737 return HDD_TSF_OP_FAIL;
738 }
739
740 if (!hdd_tsf_is_initialized(adapter)) {
741 buf[0] = TSF_NOT_READY;
742 return HDD_TSF_OP_SUCC;
743 }
744
745 buf[0] = hdd_tsf_check_conn_state(adapter);
746 if (buf[0] != TSF_RETURN)
747 return HDD_TSF_OP_SUCC;
748
749 if (qdf_atomic_inc_return(&hddctx->tsf.cap_tsf_flag) > 1) {
750 hdd_err("current in capture state");
751 buf[0] = TSF_CURRENT_IN_CAP_STATE;
752 return HDD_TSF_OP_SUCC;
753 }
754
755 /* record adapter for cap_tsf_irq_handler */
756 hddctx->tsf.cap_tsf_context = adapter;
757
758 hdd_debug("+ioctl issue cap tsf cmd");
759 cap_timer = &adapter->tsf.host_capture_req_timer;
760 qdf_mc_timer_start(cap_timer, WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS);
761
762 /* Reset TSF value for new capture */
763 adapter->tsf.cur_target_time = 0;
764
765 buf[0] = TSF_RETURN;
766
767 if (hdd_tsf_cap_sync_send(adapter))
768 return HDD_TSF_OP_SUCC;
769
770 ret = _hdd_capture_tsf_internal(adapter, buf, len);
771 hdd_debug("-ioctl return cap tsf cmd");
772
773 return ret;
774 }
775
hdd_indicate_tsf_internal(struct hdd_adapter * adapter,struct hdd_tsf_op_response * tsf_op_resp)776 static enum hdd_tsf_op_result hdd_indicate_tsf_internal(
777 struct hdd_adapter *adapter, struct hdd_tsf_op_response *tsf_op_resp)
778 {
779 int ret;
780 struct hdd_context *hddctx;
781
782 if (!adapter || !tsf_op_resp) {
783 hdd_err("invalid pointer");
784 return HDD_TSF_OP_FAIL;
785 }
786
787 hddctx = WLAN_HDD_GET_CTX(adapter);
788 if (!hddctx) {
789 hdd_err("invalid hdd context");
790 return HDD_TSF_OP_FAIL;
791 }
792
793 memset(tsf_op_resp, 0, sizeof(*tsf_op_resp));
794 if (!hdd_tsf_is_initialized(adapter)) {
795 tsf_op_resp->status = TSF_NOT_READY;
796 return HDD_TSF_OP_SUCC;
797 }
798
799 tsf_op_resp->status = hdd_tsf_check_conn_state(adapter);
800 if (tsf_op_resp->status != TSF_RETURN)
801 return HDD_TSF_OP_SUCC;
802
803 if (adapter->tsf.cur_target_time == 0) {
804 hdd_info("TSF value not received");
805 tsf_op_resp->status = TSF_NOT_RETURNED_BY_FW;
806 return HDD_TSF_OP_SUCC;
807 }
808
809 tsf_op_resp->status = TSF_RETURN;
810 tsf_op_resp->time = adapter->tsf.cur_target_time;
811 tsf_op_resp->soc_time = adapter->tsf.cur_tsf_sync_soc_time;
812
813 if (!qdf_atomic_read(&hddctx->tsf.cap_tsf_flag)) {
814 hdd_debug("old: status=%u, tsf_time=%llu, tsf_soc_time=%llu",
815 tsf_op_resp->status,
816 tsf_op_resp->time,
817 tsf_op_resp->soc_time);
818 return HDD_TSF_OP_SUCC;
819 }
820
821 ret = hdd_tsf_reset_gpio(adapter);
822 if (0 != ret) {
823 hdd_err("reset tsf gpio fail");
824 tsf_op_resp->status = TSF_RESET_GPIO_FAIL;
825 return HDD_TSF_OP_SUCC;
826 }
827 hddctx->tsf.cap_tsf_context = NULL;
828 qdf_atomic_set(&hddctx->tsf.cap_tsf_flag, 0);
829 hdd_debug("get tsf cmd,status=%u, tsf_time=%llu, tsf_soc_time=%llu",
830 tsf_op_resp->status,
831 tsf_op_resp->time,
832 tsf_op_resp->soc_time);
833
834 return HDD_TSF_OP_SUCC;
835 }
836
837 #ifdef WLAN_FEATURE_TSF_PLUS
838 /* unit for target time: us; host time: ns */
839 #define HOST_TO_TARGET_TIME_RATIO NSEC_PER_USEC
840 #define MAX_ALLOWED_DEVIATION_NS (100 * NSEC_PER_USEC)
841 #define MAX_CONTINUOUS_ERROR_CNT 3
842
843 /* to distinguish 32-bit overflow case, this interval should:
844 * equal or less than (1/2 * OVERFLOW_INDICATOR32 us)
845 */
846 #if defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ) || \
847 defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC)
848 #define WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC 2
849 #else
850 #define WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC 4
851 #endif
852 #define OVERFLOW_INDICATOR32 (((int64_t)0x1) << 32)
853 #define CAP_TSF_TIMER_FIX_SEC 1
854
855 /**
856 * enum hdd_ts_status - timestamp status
857 * @HDD_TS_STATUS_WAITING: one of the stamp-pair is not updated
858 * @HDD_TS_STATUS_READY: valid tstamp-pair
859 * @HDD_TS_STATUS_INVALID: invalid tstamp-pair
860 */
861 enum hdd_ts_status {
862 HDD_TS_STATUS_WAITING,
863 HDD_TS_STATUS_READY,
864 HDD_TS_STATUS_INVALID
865 };
866
867 static
__hdd_start_tsf_sync(struct hdd_adapter * adapter)868 enum hdd_tsf_op_result __hdd_start_tsf_sync(struct hdd_adapter *adapter)
869 {
870 QDF_STATUS ret;
871
872 if (!hdd_get_th_sync_status(adapter)) {
873 hdd_err("Host Target sync has not initialized");
874 return HDD_TSF_OP_FAIL;
875 }
876
877 hdd_tsf_gpio_sync_work_init(adapter);
878 ret = qdf_mc_timer_start(&adapter->tsf.host_target_sync_timer,
879 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS);
880 if (ret != QDF_STATUS_SUCCESS && ret != QDF_STATUS_E_ALREADY) {
881 hdd_err("Failed to start timer, ret: %d", ret);
882 return HDD_TSF_OP_FAIL;
883 }
884 return HDD_TSF_OP_SUCC;
885 }
886
887 static
__hdd_stop_tsf_sync(struct hdd_adapter * adapter)888 enum hdd_tsf_op_result __hdd_stop_tsf_sync(struct hdd_adapter *adapter)
889 {
890 QDF_STATUS ret;
891 struct hdd_context *hdd_ctx;
892
893 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
894 if (!hdd_ctx) {
895 hdd_err("invalid hdd context");
896 return HDD_TSF_OP_FAIL;
897 }
898
899 if (!hdd_get_th_sync_status(adapter)) {
900 hdd_debug("Host Target sync has not initialized");
901 return HDD_TSF_OP_SUCC;
902 }
903
904 ret = qdf_mc_timer_stop(&adapter->tsf.host_target_sync_timer);
905 if (ret != QDF_STATUS_SUCCESS) {
906 hdd_err("Failed to stop target timer, ret: %d", ret);
907 return HDD_TSF_OP_FAIL;
908 }
909
910 ret = qdf_mc_timer_stop(&adapter->tsf.host_capture_req_timer);
911 if (ret != QDF_STATUS_SUCCESS) {
912 hdd_err("Failed to stop capture timer, ret: %d", ret);
913 return HDD_TSF_OP_FAIL;
914 }
915
916 hdd_tsf_stop_ext_gpio_sync(adapter);
917 hdd_tsf_gpio_sync_work_deinit(adapter);
918 return HDD_TSF_OP_SUCC;
919 }
920
hdd_reset_timestamps(struct hdd_adapter * adapter)921 static inline void hdd_reset_timestamps(struct hdd_adapter *adapter)
922 {
923 struct hdd_vdev_tsf *tsf = &adapter->tsf;
924
925 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
926 tsf->cur_host_time = 0;
927 tsf->cur_target_time = 0;
928 tsf->last_host_time = 0;
929 tsf->last_target_time = 0;
930 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
931 }
932
933 /**
934 * hdd_check_timestamp_status() - return the tstamp status
935 * @last_target_time: the last saved target time
936 * @last_sync_time: the last saved sync time
937 * @cur_target_time: new target time
938 * @cur_sync_time: new sync time
939 * @force_sync: flag to force new timestamp-pair as valid
940 *
941 * This function check the new timstamp-pair(cur_host_time/cur_target_time)or
942 * (cur_qtime_time/cur_target_time)
943 * Return:
944 * HDD_TS_STATUS_WAITING: cur_sync_time or cur_sync_time is 0
945 * HDD_TS_STATUS_READY: cur_target_time/cur_host_time is a valid pair,
946 * and can be saved
947 * HDD_TS_STATUS_INVALID: cur_target_time/cur_sync_time is a invalid pair,
948 * should be discard
949 */
950 static
hdd_check_timestamp_status(uint64_t last_target_time,uint64_t last_sync_time,uint64_t cur_target_time,uint64_t cur_sync_time,bool force_sync)951 enum hdd_ts_status hdd_check_timestamp_status(
952 uint64_t last_target_time,
953 uint64_t last_sync_time,
954 uint64_t cur_target_time,
955 uint64_t cur_sync_time,
956 bool force_sync)
957 {
958 uint64_t delta_ns, delta_target_time, delta_sync_time;
959
960 /* one or more are not updated, need to wait */
961 if (cur_target_time == 0 || cur_sync_time == 0)
962 return HDD_TS_STATUS_WAITING;
963
964 /* init value, it's the first time to update the pair */
965 if (last_target_time == 0 && last_sync_time == 0)
966 return HDD_TS_STATUS_READY;
967
968 /* the new values should be greater than the saved values */
969 if ((cur_target_time <= last_target_time) ||
970 (cur_sync_time <= last_sync_time)) {
971 hdd_err("Invalid timestamps!last_target_time: %llu;"
972 "last_sync_time: %llu; cur_target_time: %llu;"
973 "cur_sync_time: %llu",
974 last_target_time, last_sync_time,
975 cur_target_time, cur_sync_time);
976 return HDD_TS_STATUS_INVALID;
977 }
978
979 delta_target_time = (cur_target_time - last_target_time) *
980 NSEC_PER_USEC;
981 delta_sync_time = cur_sync_time - last_sync_time;
982
983 /*
984 * DO NOT use abs64() , a big uint64 value might be turned to
985 * a small int64 value
986 */
987 delta_ns = ((delta_target_time > delta_sync_time) ?
988 (delta_target_time - delta_sync_time) :
989 (delta_sync_time - delta_target_time));
990 hdd_debug("timestamps deviation - delta: %llu ns", delta_ns);
991 /* the deviation should be smaller than a threshold */
992 if (!force_sync && delta_ns > MAX_ALLOWED_DEVIATION_NS) {
993 hdd_debug("Invalid timestamps - delta: %llu ns", delta_ns);
994 return HDD_TS_STATUS_INVALID;
995 }
996 return HDD_TS_STATUS_READY;
997 }
998
hdd_tsf_is_in_cap(struct hdd_adapter * adapter)999 static inline bool hdd_tsf_is_in_cap(struct hdd_adapter *adapter)
1000 {
1001 struct hdd_context *hddctx;
1002
1003 hddctx = WLAN_HDD_GET_CTX(adapter);
1004 if (!hddctx)
1005 return false;
1006
1007 return qdf_atomic_read(&hddctx->tsf.cap_tsf_flag) > 0;
1008 }
1009
1010 /* define 64bit plus/minus to deal with overflow */
hdd_64bit_plus(uint64_t x,int64_t y,uint64_t * ret)1011 static inline int hdd_64bit_plus(uint64_t x, int64_t y, uint64_t *ret)
1012 {
1013 if ((y < 0 && (-y) > x) ||
1014 (y > 0 && (y > U64_MAX - x))) {
1015 *ret = 0;
1016 return -EINVAL;
1017 }
1018
1019 *ret = x + y;
1020 return 0;
1021 }
1022
hdd_uint64_plus(uint64_t x,uint64_t y,uint64_t * ret)1023 static inline int hdd_uint64_plus(uint64_t x, uint64_t y, uint64_t *ret)
1024 {
1025 if (!ret)
1026 return -EINVAL;
1027
1028 if (x > (U64_MAX - y)) {
1029 *ret = 0;
1030 return -EINVAL;
1031 }
1032
1033 *ret = x + y;
1034 return 0;
1035 }
1036
hdd_uint64_minus(uint64_t x,uint64_t y,uint64_t * ret)1037 static inline int hdd_uint64_minus(uint64_t x, uint64_t y, uint64_t *ret)
1038 {
1039 if (!ret)
1040 return -EINVAL;
1041
1042 if (x < y) {
1043 *ret = 0;
1044 return -EINVAL;
1045 }
1046
1047 *ret = x - y;
1048 return 0;
1049 }
1050
hdd_get_hosttime_from_targettime(struct hdd_adapter * adapter,uint64_t target_time,uint64_t * host_time)1051 static inline int32_t hdd_get_hosttime_from_targettime(
1052 struct hdd_adapter *adapter, uint64_t target_time,
1053 uint64_t *host_time)
1054 {
1055 struct hdd_vdev_tsf *tsf;
1056 int32_t ret = -EINVAL;
1057 int64_t delta32_target;
1058 bool in_cap_state;
1059 int64_t normal_interval_target;
1060
1061 in_cap_state = hdd_tsf_is_in_cap(adapter);
1062 tsf = &adapter->tsf;
1063
1064 /*
1065 * To avoid check the lock when it's not capturing tsf
1066 * (the tstamp-pair won't be changed)
1067 */
1068 if (in_cap_state)
1069 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1070
1071 hdd_wlan_restart_tsf_cap(adapter);
1072 /* at present, target_time is only 32bit in fact */
1073 delta32_target = (int64_t)((target_time & U32_MAX) -
1074 (tsf->last_target_time & U32_MAX));
1075
1076 normal_interval_target = WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC *
1077 qdf_do_div(NSEC_PER_SEC, HOST_TO_TARGET_TIME_RATIO);
1078
1079 if (delta32_target <
1080 (normal_interval_target - OVERFLOW_INDICATOR32))
1081 delta32_target += OVERFLOW_INDICATOR32;
1082 else if (delta32_target >
1083 (OVERFLOW_INDICATOR32 - normal_interval_target))
1084 delta32_target -= OVERFLOW_INDICATOR32;
1085
1086 ret = hdd_64bit_plus(tsf->last_host_time,
1087 HOST_TO_TARGET_TIME_RATIO * delta32_target,
1088 host_time);
1089
1090 if (in_cap_state)
1091 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1092
1093 return ret;
1094 }
1095
hdd_get_targettime_from_hosttime(struct hdd_adapter * adapter,uint64_t host_time,uint64_t * target_time)1096 static inline int32_t hdd_get_targettime_from_hosttime(
1097 struct hdd_adapter *adapter, uint64_t host_time,
1098 uint64_t *target_time)
1099 {
1100 struct hdd_vdev_tsf *tsf;
1101 int32_t ret = -EINVAL;
1102 bool in_cap_state;
1103
1104 if (!adapter || host_time == 0)
1105 return ret;
1106
1107 tsf = &adapter->tsf;
1108 in_cap_state = hdd_tsf_is_in_cap(adapter);
1109 if (in_cap_state)
1110 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1111
1112 if (host_time < tsf->last_host_time)
1113 ret = hdd_uint64_minus(tsf->last_target_time,
1114 qdf_do_div(tsf->last_host_time -
1115 host_time,
1116 HOST_TO_TARGET_TIME_RATIO),
1117 target_time);
1118 else
1119 ret = hdd_uint64_plus(tsf->last_target_time,
1120 qdf_do_div(host_time -
1121 tsf->last_host_time,
1122 HOST_TO_TARGET_TIME_RATIO),
1123 target_time);
1124
1125 if (in_cap_state)
1126 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1127
1128 return ret;
1129 }
1130
1131 /**
1132 * hdd_get_soctime_from_tsf64time() - return get status
1133 *
1134 * @adapter: Adapter pointer
1135 * @tsf64_time: current tsf64time, us
1136 * @soc_time: current soc time(qtime), ns
1137 *
1138 * This function get current soc time from current tsf64 time
1139 * Returun int32_t value to tell get success or fail.
1140 *
1141 * Return:
1142 * 0: success
1143 * other: fail
1144 *
1145 */
hdd_get_soctime_from_tsf64time(struct hdd_adapter * adapter,uint64_t tsf64_time,uint64_t * soc_time)1146 static inline int32_t hdd_get_soctime_from_tsf64time(
1147 struct hdd_adapter *adapter, uint64_t tsf64_time,
1148 uint64_t *soc_time)
1149 {
1150 struct hdd_vdev_tsf *tsf;
1151 int32_t ret = -EINVAL;
1152 uint64_t delta64_tsf64time;
1153 uint64_t delta64_soctime;
1154 bool in_cap_state;
1155
1156 in_cap_state = hdd_tsf_is_in_cap(adapter);
1157 tsf = &adapter->tsf;
1158
1159 /*
1160 * To avoid check the lock when it's not capturing tsf
1161 * (the tstamp-pair won't be changed)
1162 */
1163 if (in_cap_state)
1164 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1165
1166 /* at present, target_time is 64bit (g_tsf64), us*/
1167 if (tsf64_time > tsf->last_target_global_tsf_time) {
1168 delta64_tsf64time = tsf64_time -
1169 tsf->last_target_global_tsf_time;
1170 delta64_soctime = delta64_tsf64time * NSEC_PER_USEC;
1171
1172 /* soc_time (ns)*/
1173 ret = hdd_uint64_plus(tsf->last_tsf_sync_soc_time,
1174 delta64_soctime, soc_time);
1175 } else {
1176 delta64_tsf64time = tsf->last_target_global_tsf_time -
1177 tsf64_time;
1178 delta64_soctime = delta64_tsf64time * NSEC_PER_USEC;
1179
1180 /* soc_time (ns)*/
1181 ret = hdd_uint64_minus(tsf->last_tsf_sync_soc_time,
1182 delta64_soctime, soc_time);
1183 }
1184
1185 if (in_cap_state)
1186 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1187
1188 return ret;
1189 }
1190
1191 /**
1192 * hdd_get_tsftime_from_qtime()
1193 *
1194 * @adapter: Adapter pointer
1195 * @qtime: current qtime, us
1196 * @tsf_time: current tsf time(qtime), us
1197 *
1198 * This function determines current tsf time
1199 * using current qtime
1200 *
1201 * Return: 0 for success or non-zero negative failure code
1202 */
1203 static inline int32_t
hdd_get_tsftime_from_qtime(struct hdd_adapter * adapter,uint64_t qtime,uint64_t * tsf_time)1204 hdd_get_tsftime_from_qtime(struct hdd_adapter *adapter, uint64_t qtime,
1205 uint64_t *tsf_time)
1206 {
1207 struct hdd_vdev_tsf *tsf;
1208 int32_t ret = -EINVAL;
1209 uint64_t delta64_tsf64time, tsf_sync_qtime;
1210 bool in_cap_state;
1211
1212 in_cap_state = hdd_tsf_is_in_cap(adapter);
1213 tsf = &adapter->tsf;
1214
1215 /*
1216 * To avoid check the lock when it's not capturing tsf
1217 * (the tstamp-pair won't be changed)
1218 */
1219 if (in_cap_state)
1220 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1221
1222 tsf_sync_qtime = tsf->last_tsf_sync_soc_time;
1223 tsf_sync_qtime = qdf_do_div(tsf_sync_qtime, NSEC_PER_USEC);
1224
1225 if (qtime > tsf_sync_qtime) {
1226 delta64_tsf64time = qtime - tsf_sync_qtime;
1227 ret = hdd_uint64_plus(tsf->last_target_time,
1228 delta64_tsf64time, tsf_time);
1229 } else {
1230 delta64_tsf64time = tsf_sync_qtime - qtime;
1231 ret = hdd_uint64_minus(tsf->last_target_time,
1232 delta64_tsf64time, tsf_time);
1233 }
1234
1235 if (in_cap_state)
1236 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1237
1238 return ret;
1239 }
1240
hdd_get_tsf_time(void * adapter_ctx,uint64_t input_time,uint64_t * tsf_time)1241 QDF_STATUS hdd_get_tsf_time(void *adapter_ctx, uint64_t input_time,
1242 uint64_t *tsf_time)
1243 {
1244 struct hdd_adapter *adapter;
1245 uint64_t qtime;
1246
1247 /* Sanity check on inputs */
1248 if (unlikely((!adapter_ctx) || (!input_time))) {
1249 hdd_err("Invalid param passed");
1250 return QDF_STATUS_E_FAILURE;
1251 }
1252
1253 adapter = (struct hdd_adapter *)adapter_ctx;
1254 if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
1255 hdd_err("Magic cookie(%x) for adapter sanity verification is invalid",
1256 adapter->magic);
1257 return QDF_STATUS_E_FAILURE;
1258 }
1259
1260 qtime = qdf_log_timestamp_to_usecs(input_time);
1261 hdd_get_tsftime_from_qtime(adapter, qtime, tsf_time);
1262 return QDF_STATUS_SUCCESS;
1263 }
1264
hdd_capture_tsf_timer_expired_handler(void * arg)1265 static void hdd_capture_tsf_timer_expired_handler(void *arg)
1266 {
1267 uint32_t tsf_op_resp;
1268 struct hdd_adapter *adapter;
1269
1270 if (!arg)
1271 return;
1272
1273 adapter = (struct hdd_adapter *)arg;
1274 hdd_capture_tsf_internal(adapter, &tsf_op_resp, 1);
1275 }
1276
1277 #ifdef WLAN_FEATURE_TSF_ACCURACY
1278 #define WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC 50
1279 #define WLAN_HDD_TOGGLE_GPIO_BACKOFF_MAX_USEC 200
1280 #define WLAN_HDD_PULSE_WIDTH_MSEC 1
1281
1282 /**
1283 * hdd_get_tsf_accuracy_context() - Return the TSF Accuracy config params
1284 * @adapter: Pointer to adapter
1285 *
1286 * This function validates feature config parameters
1287 *
1288 * Return: Pointer to TSF Accuracy feature configs
1289 */
1290 static struct wlan_fwol_tsf_accuracy_configs *
hdd_get_tsf_accuracy_context(struct hdd_adapter * adapter)1291 hdd_get_tsf_accuracy_context(struct hdd_adapter *adapter)
1292 {
1293 struct wlan_fwol_tsf_accuracy_configs *configs = NULL;
1294 struct hdd_context *hddctx;
1295 int status;
1296
1297 hddctx = WLAN_HDD_GET_CTX(adapter);
1298 if (!hddctx) {
1299 hdd_err("invalid hdd context");
1300 return NULL;
1301 }
1302
1303 if (hddctx->tsf.tsf_accuracy_context &&
1304 hddctx->tsf.tsf_accuracy_context != adapter)
1305 return NULL;
1306
1307 status = ucfg_fwol_get_tsf_accuracy_configs(hddctx->psoc, &configs);
1308 if (status == QDF_STATUS_E_FAILURE)
1309 return NULL;
1310
1311 if (!configs || !configs->enable ||
1312 (configs->periodic_pulse_gpio == TSF_GPIO_PIN_INVALID &&
1313 configs->sync_gpio == TSF_GPIO_PIN_INVALID))
1314 return NULL;
1315
1316 return configs;
1317 }
1318
1319 /**
1320 * hdd_tsf_gpio_pulse() - Raise pulse of WLAN_HDD_PULSE_WIDTH_MSEC on gpio
1321 * @gpio_num: GPIO number
1322 *
1323 * Return: None
1324 */
hdd_tsf_gpio_pulse(uint32_t gpio_num)1325 static void hdd_tsf_gpio_pulse(uint32_t gpio_num)
1326 {
1327 if (gpio_num == TSF_GPIO_PIN_INVALID)
1328 return;
1329
1330 gpio_set_value(gpio_num, OUTPUT_HIGH);
1331 udelay(WLAN_HDD_PULSE_WIDTH_MSEC * USEC_PER_MSEC);
1332 gpio_set_value(gpio_num, OUTPUT_LOW);
1333 }
1334
1335 /**
1336 * hdd_tsf_gpio_timer_expired_handler() - Handle periodic TSF periodic expiry
1337 * @arg: Pointer to qdf_hrtimer_data_t
1338 *
1339 * Raise GPIO pulse on TSF time cycle completion and schedules hrtimer for
1340 * next cycle. Also, monitors drift between Host time and TSF time.
1341 * This data will be used for scheduling hrtimer expiry.
1342 *
1343 * Return:
1344 * QDF_HRTIMER_RESTART - On completion of TSF cycle processing
1345 * QDF_HRTIMER_NORESTART - On error
1346 */
1347 static enum qdf_hrtimer_restart_status
hdd_tsf_gpio_timer_expired_handler(qdf_hrtimer_data_t * arg)1348 hdd_tsf_gpio_timer_expired_handler(qdf_hrtimer_data_t *arg)
1349 {
1350 struct hdd_adapter *adapter;
1351 struct hdd_vdev_tsf *tsf;
1352 struct wlan_fwol_tsf_accuracy_configs *configs;
1353 qdf_ktime_t cur_qtime, spin_until, next_ktime;
1354 uint64_t qtime;
1355 uint64_t tsf_time_us;
1356 uint32_t elapsed_time_us;
1357 uint32_t remaining_time_us;
1358 uint32_t delta_interval_us;
1359
1360 tsf = qdf_container_of(arg, struct hdd_vdev_tsf,
1361 host_trigger_gpio_timer);
1362 if (!tsf)
1363 return QDF_HRTIMER_NORESTART;
1364
1365 adapter = qdf_container_of(tsf, struct hdd_adapter, tsf);
1366
1367 configs = hdd_get_tsf_accuracy_context(adapter);
1368 if (!configs)
1369 return QDF_HRTIMER_NORESTART;
1370
1371 /* Get current System and TSF mapping */
1372 qtime = qdf_log_timestamp_to_usecs(qdf_get_log_timestamp());
1373 hdd_get_tsftime_from_qtime(adapter, qtime, &tsf_time_us);
1374 elapsed_time_us = (uint32_t)
1375 (tsf_time_us % (configs->pulse_interval_ms * USEC_PER_MSEC));
1376 remaining_time_us =
1377 (configs->pulse_interval_ms * USEC_PER_MSEC) - elapsed_time_us;
1378
1379 /* Skip raising GPIO pulse in case of TSF cycle already completed */
1380 if (elapsed_time_us < remaining_time_us) {
1381 next_ktime = qdf_ns_to_ktime(NSEC_PER_USEC *
1382 ((configs->pulse_interval_ms * USEC_PER_MSEC) -
1383 WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC -
1384 elapsed_time_us));
1385 hdd_debug("TSF_Accuracy: skip GPIO pulse tsf_time_us:%llu",
1386 tsf_time_us);
1387 goto end;
1388 }
1389
1390 if (remaining_time_us > WLAN_HDD_TOGGLE_GPIO_BACKOFF_MAX_USEC)
1391 goto skip;
1392 /*
1393 * Expect WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC seconds of backoff always
1394 * for TSF time to complete a cycle of given interval.
1395 * Hence run backoff busy wait and then trigger GPIO
1396 */
1397 cur_qtime = qdf_ns_to_ktime(qtime * NSEC_PER_USEC);
1398 spin_until = qdf_ktime_add(cur_qtime,
1399 qdf_ns_to_ktime(remaining_time_us *
1400 NSEC_PER_USEC));
1401 do {
1402 qtime = qdf_log_timestamp_to_usecs(qdf_get_log_timestamp());
1403 cur_qtime = qdf_ns_to_ktime(qtime * NSEC_PER_USEC);
1404 } while (ktime_compare(cur_qtime, spin_until) < 0);
1405
1406 /* Toggle GPIO */
1407 hdd_tsf_gpio_pulse(configs->periodic_pulse_gpio);
1408
1409 /* Check current system and TSF mapping for logging */
1410 hdd_get_tsftime_from_qtime(adapter, qtime, &tsf_time_us);
1411
1412 hdd_debug("TSF_Accuracy: GPIO toggled log_time_us:%llu, tsf_time_us:%llu, slept_us:%d",
1413 qtime, tsf_time_us, remaining_time_us);
1414
1415 /*
1416 * Schedule next GPIO toggle by adding to last expiry. Monitor drift
1417 * and adjust next expiry time based on system and TSF clock
1418 * difference.
1419 */
1420 skip:
1421 if (remaining_time_us > WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC) {
1422 delta_interval_us = remaining_time_us -
1423 WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC;
1424 next_ktime = qdf_ns_to_ktime(NSEC_PER_USEC *
1425 ((configs->pulse_interval_ms *
1426 USEC_PER_MSEC) +
1427 delta_interval_us));
1428 } else {
1429 next_ktime = configs->pulse_interval_ms;
1430 }
1431 end:
1432 qdf_hrtimer_add_expires(&adapter->tsf.host_trigger_gpio_timer,
1433 next_ktime);
1434
1435 return QDF_HRTIMER_RESTART;
1436 }
1437
1438 /**
1439 * hdd_tsf_setup_gpio_toggle() - Schedules hrtimer for TSF periodic processing.
1440 * @adapter: Pointer to adapter
1441 *
1442 * Schedule a TSF time domain periodic pulse handling and also indicate a
1443 * TSF sync done by toggling GPIO.
1444 *
1445 * Return: None
1446 */
hdd_tsf_setup_gpio_toggle(struct hdd_adapter * adapter)1447 static void hdd_tsf_setup_gpio_toggle(struct hdd_adapter *adapter)
1448 {
1449 static uint32_t gpio_state = OUTPUT_LOW;
1450 uint64_t tsf_time_us;
1451 uint64_t qtime;
1452 uint32_t elapsed_time_us;
1453 uint32_t remaining_time_us;
1454 qdf_ktime_t cur_ktime, next_ktime;
1455 struct wlan_fwol_tsf_accuracy_configs *configs;
1456 qdf_hrtimer_data_t *gtimer;
1457
1458 configs = hdd_get_tsf_accuracy_context(adapter);
1459 if (!configs)
1460 return;
1461
1462 gtimer = &adapter->tsf.host_trigger_gpio_timer;
1463
1464 /* Get current System and TSF mapping */
1465 cur_ktime = qdf_ktime_get();
1466 qtime = qdf_log_timestamp_to_usecs(qdf_get_log_timestamp());
1467 hdd_get_tsftime_from_qtime(adapter, qtime, &tsf_time_us);
1468
1469 if (configs->sync_gpio != TSF_GPIO_PIN_INVALID) {
1470 if (gpio_state == OUTPUT_LOW)
1471 gpio_state = OUTPUT_HIGH;
1472 else
1473 gpio_state = OUTPUT_LOW;
1474 gpio_set_value(configs->sync_gpio, gpio_state);
1475 }
1476
1477 hdd_debug("TSF_Accuracy: TSF sync done system_time_us:%llu, log_time_us:%llu, tsf_time_us:%llu",
1478 qdf_ktime_to_us(cur_ktime), qtime, tsf_time_us);
1479
1480 /* Start timer if it is not scheduled yet */
1481 if (!(qdf_hrtimer_is_queued(gtimer) ||
1482 qdf_hrtimer_callback_running(gtimer))) {
1483 /*
1484 * Take out WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC as backoff timer
1485 * which is taken care by hrtimer handler
1486 */
1487 elapsed_time_us = (uint32_t)(tsf_time_us % USEC_PER_SEC);
1488 remaining_time_us = USEC_PER_SEC - elapsed_time_us;
1489 if (remaining_time_us <= WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC)
1490 return;
1491 next_ktime = qdf_ktime_add(cur_ktime,
1492 qdf_ns_to_ktime((remaining_time_us -
1493 WLAN_HDD_TOGGLE_GPIO_BACKOFF_USEC) * NSEC_PER_USEC));
1494 qdf_hrtimer_start(gtimer, next_ktime, QDF_HRTIMER_MODE_ABS);
1495 }
1496 }
1497
1498 /**
1499 * hdd_tsf_regular_gpio_pulse_init() - Initialize TSF Accuracy feature
1500 * @adapter: Pointer to adapter
1501 *
1502 * Return: None
1503 */
hdd_tsf_regular_gpio_pulse_init(struct hdd_adapter * adapter)1504 static void hdd_tsf_regular_gpio_pulse_init(struct hdd_adapter *adapter)
1505 {
1506 struct wlan_fwol_tsf_accuracy_configs *configs;
1507 struct hdd_context *hddctx;
1508
1509 hddctx = WLAN_HDD_GET_CTX(adapter);
1510 if (!hddctx) {
1511 hdd_err("invalid hdd context");
1512 return;
1513 }
1514
1515 configs = hdd_get_tsf_accuracy_context(adapter);
1516 if (!configs)
1517 goto fail;
1518
1519 qdf_hrtimer_init(&adapter->tsf.host_trigger_gpio_timer,
1520 hdd_tsf_gpio_timer_expired_handler,
1521 QDF_CLOCK_MONOTONIC, QDF_HRTIMER_MODE_ABS,
1522 QDF_CONTEXT_HARDWARE);
1523
1524 if (configs->periodic_pulse_gpio != TSF_GPIO_PIN_INVALID) {
1525 if (gpio_request(configs->periodic_pulse_gpio,
1526 "tsf_periodic_pulse"))
1527 goto fail;
1528 if (gpio_direction_output(configs->periodic_pulse_gpio,
1529 OUTPUT_LOW))
1530 goto fail_free_pulse_gpio;
1531 }
1532
1533 if (configs->sync_gpio != TSF_GPIO_PIN_INVALID) {
1534 if (gpio_request(configs->sync_gpio, "tsf_sync_toggle"))
1535 goto fail_free_pulse_gpio;
1536 if (gpio_direction_output(configs->sync_gpio, OUTPUT_LOW))
1537 goto fail_free_gpio;
1538 }
1539
1540 hddctx->tsf.tsf_accuracy_context = adapter;
1541 hdd_debug("TSF_Accuracy: Feature initialization success");
1542 return;
1543
1544 fail_free_gpio:
1545 gpio_free(configs->sync_gpio);
1546 fail_free_pulse_gpio:
1547 if (configs->periodic_pulse_gpio != TSF_GPIO_PIN_INVALID)
1548 gpio_free(configs->periodic_pulse_gpio);
1549 fail:
1550 hdd_err("TSF_Accuracy: Feature init failed");
1551 }
1552
1553 /**
1554 * hdd_tsf_regular_gpio_pulse_deinit() - Deactivate TSF Accuracy feature
1555 * @adapter: Pointer to adapter
1556 *
1557 * Return: None
1558 */
hdd_tsf_regular_gpio_pulse_deinit(struct hdd_adapter * adapter)1559 static void hdd_tsf_regular_gpio_pulse_deinit(struct hdd_adapter *adapter)
1560 {
1561 struct wlan_fwol_tsf_accuracy_configs *configs = NULL;
1562 struct hdd_context *hddctx;
1563
1564 hddctx = WLAN_HDD_GET_CTX(adapter);
1565 if (!hddctx) {
1566 hdd_err("invalid hdd context");
1567 return;
1568 }
1569
1570 if (hddctx->tsf.tsf_accuracy_context != adapter)
1571 return;
1572
1573 configs = hdd_get_tsf_accuracy_context(adapter);
1574 if (!configs)
1575 return;
1576
1577 qdf_hrtimer_cancel(&adapter->tsf.host_trigger_gpio_timer);
1578
1579 if (configs->periodic_pulse_gpio != TSF_GPIO_PIN_INVALID)
1580 gpio_free(configs->periodic_pulse_gpio);
1581 if (configs->sync_gpio != TSF_GPIO_PIN_INVALID) {
1582 gpio_set_value(configs->sync_gpio, OUTPUT_LOW);
1583 gpio_free(configs->sync_gpio);
1584 }
1585
1586 hddctx->tsf.tsf_accuracy_context = NULL;
1587 }
1588 #else
hdd_tsf_setup_gpio_toggle(struct hdd_adapter * adapter)1589 static void hdd_tsf_setup_gpio_toggle(struct hdd_adapter *adapter)
1590 {
1591 }
1592
hdd_tsf_regular_gpio_pulse_init(struct hdd_adapter * adapter)1593 static void hdd_tsf_regular_gpio_pulse_init(struct hdd_adapter *adapter)
1594 {
1595 }
1596
hdd_tsf_regular_gpio_pulse_deinit(struct hdd_adapter * adapter)1597 static void hdd_tsf_regular_gpio_pulse_deinit(struct hdd_adapter *adapter)
1598 {
1599 }
1600 #endif
1601
1602 #ifndef WLAN_FEATURE_TSF_PLUS_NOIRQ
1603 #if !defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC) && \
1604 !defined(WLAN_FEATURE_TSF_TIMER_SYNC)
1605
hdd_tsf_captured_irq_handler(int irq,void * arg)1606 static irqreturn_t hdd_tsf_captured_irq_handler(int irq, void *arg)
1607 {
1608 struct hdd_adapter *adapter;
1609 struct hdd_context *hdd_ctx;
1610 uint64_t host_time;
1611 char *name = NULL;
1612
1613 if (!arg)
1614 return IRQ_NONE;
1615
1616 if (irq != tsf_gpio_irq_num)
1617 return IRQ_NONE;
1618
1619 hdd_ctx = (struct hdd_context *)arg;
1620 host_time = hdd_get_monotonic_host_time(hdd_ctx);
1621
1622 adapter = hdd_ctx->tsf.cap_tsf_context;
1623 if (!adapter)
1624 return IRQ_HANDLED;
1625
1626 if (!hdd_tsf_is_initialized(adapter)) {
1627 hdd_err("tsf is not init, ignore irq");
1628 return IRQ_HANDLED;
1629 }
1630
1631 hdd_update_timestamp(adapter, 0, host_time);
1632 if (adapter->dev)
1633 name = adapter->dev->name;
1634
1635 hdd_debug("irq: %d - iface: %s - host_time: %llu",
1636 irq, (!name ? "none" : name), host_time);
1637
1638 return IRQ_HANDLED;
1639 }
1640 #endif
1641 #endif
1642
hdd_capture_req_timer_expired_handler(void * arg)1643 void hdd_capture_req_timer_expired_handler(void *arg)
1644 {
1645 struct hdd_adapter *adapter;
1646 struct hdd_context *hdd_ctx;
1647 struct hdd_vdev_tsf *tsf;
1648 QDF_TIMER_STATE capture_req_timer_status;
1649 qdf_mc_timer_t *sync_timer;
1650 int interval;
1651 int ret;
1652
1653 if (!arg)
1654 return;
1655 adapter = (struct hdd_adapter *)arg;
1656
1657 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1658 if (!hdd_ctx) {
1659 hdd_warn("invalid hdd context");
1660 return;
1661 }
1662 tsf = &adapter->tsf;
1663 if (!hdd_tsf_is_initialized(adapter)) {
1664 hdd_warn("tsf not init");
1665 return;
1666 }
1667
1668 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1669 tsf->cur_host_time = 0;
1670 tsf->cur_target_time = 0;
1671 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1672
1673 ret = hdd_tsf_reset_gpio(adapter);
1674 if (0 != ret)
1675 hdd_info("reset tsf gpio fail");
1676
1677 hdd_ctx->tsf.cap_tsf_context = NULL;
1678 qdf_atomic_set(&hdd_ctx->tsf.cap_tsf_flag, 0);
1679
1680 sync_timer = &tsf->host_target_sync_timer;
1681 capture_req_timer_status =
1682 qdf_mc_timer_get_current_state(sync_timer);
1683
1684 if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) {
1685 hdd_warn("invalid timer status");
1686 return;
1687 }
1688
1689 interval = WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL * MSEC_PER_SEC;
1690 qdf_mc_timer_start(sync_timer, interval);
1691 }
1692
1693 #if defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) || \
1694 defined(WLAN_FEATURE_TSF_TIMER_SYNC)
hdd_update_timestamp(struct hdd_adapter * adapter)1695 static void hdd_update_timestamp(struct hdd_adapter *adapter)
1696 {
1697 int interval = 0;
1698 enum hdd_ts_status sync_status;
1699 struct hdd_vdev_tsf *tsf;
1700
1701 if (!adapter)
1702 return;
1703
1704 tsf = &adapter->tsf;
1705 /* on ADREASTEA ach, Qtime is used to sync host and tsf time as a
1706 * intermedia there is no IRQ to sync up TSF-HOST, so host time in ns
1707 * and target in us will be updated at the same time in WMI command
1708 * callback
1709 */
1710
1711 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1712 sync_status =
1713 hdd_check_timestamp_status(tsf->last_target_time,
1714 tsf->last_tsf_sync_soc_time,
1715 tsf->cur_target_time,
1716 tsf->cur_tsf_sync_soc_time,
1717 tsf->host_target_sync_force);
1718 if (tsf->host_target_sync_force)
1719 tsf->host_target_sync_force = false;
1720
1721 hdd_debug("sync_status %d", sync_status);
1722 switch (sync_status) {
1723 case HDD_TS_STATUS_INVALID:
1724 if (++tsf->continuous_error_count <
1725 MAX_CONTINUOUS_ERROR_CNT) {
1726 interval =
1727 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS;
1728 tsf->cur_target_time = 0;
1729 tsf->cur_tsf_sync_soc_time = 0;
1730 break;
1731 }
1732 hdd_debug("Reach the max continuous error count");
1733
1734 /* If reach MAX_CONTINUOUS_ERROR_CNT, treat it as valid pair */
1735 fallthrough;
1736 case HDD_TS_STATUS_READY:
1737 tsf->last_target_time = tsf->cur_target_time;
1738 tsf->last_target_global_tsf_time =
1739 tsf->cur_target_global_tsf_time;
1740 tsf->last_tsf_sync_soc_time =
1741 tsf->cur_tsf_sync_soc_time;
1742 tsf->cur_target_time = 0;
1743 tsf->cur_target_global_tsf_time = 0;
1744 tsf->cur_tsf_sync_soc_time = 0;
1745 hdd_debug("ts-pair updated: target: %llu; g_target:%llu, Qtime: %llu",
1746 tsf->last_target_time,
1747 tsf->last_target_global_tsf_time,
1748 tsf->last_tsf_sync_soc_time);
1749
1750 /*
1751 * TSF-HOST need to be updated in at most
1752 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, it couldn't be achieved
1753 * if the timer interval is also
1754 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, due to processing or
1755 * schedule delay. So deduct several seconds from
1756 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC.
1757 * Without this change, hdd_get_hosttime_from_targettime() will
1758 * get wrong host time when it's longer than
1759 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC from last
1760 * TSF-HOST update.
1761 */
1762
1763 if (tsf->dynamic_tsf_sync_interval)
1764 interval = tsf->dynamic_tsf_sync_interval;
1765 else
1766 interval = (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC -
1767 CAP_TSF_TIMER_FIX_SEC) * MSEC_PER_SEC;
1768
1769 tsf->continuous_error_count = 0;
1770 tsf->continuous_cap_retry_count = 0;
1771 hdd_debug("ts-pair updated: interval: %d",
1772 interval);
1773 break;
1774 case HDD_TS_STATUS_WAITING:
1775 interval = 0;
1776 hdd_warn("TS status is waiting due to one or more pair not updated");
1777 break;
1778 }
1779 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1780
1781 hdd_tsf_setup_gpio_toggle(adapter);
1782
1783 if (interval > 0)
1784 qdf_mc_timer_start(&tsf->host_target_sync_timer,
1785 interval);
1786 }
1787
__hdd_wlan_tsf_show(struct device * dev,struct device_attribute * attr,char * buf)1788 static ssize_t __hdd_wlan_tsf_show(struct device *dev,
1789 struct device_attribute *attr, char *buf)
1790 {
1791 struct hdd_station_ctx *hdd_sta_ctx;
1792 struct hdd_adapter *adapter;
1793 struct hdd_context *hdd_ctx;
1794 uint64_t tsf_sync_qtime, host_time, reg_qtime, qtime, target_time;
1795 ssize_t size;
1796 uint8_t *mac;
1797
1798 struct net_device *net_dev = container_of(dev, struct net_device, dev);
1799
1800 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
1801 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)
1802 return scnprintf(buf, PAGE_SIZE, "Invalid device\n");
1803
1804 if (!hdd_get_th_sync_status(adapter))
1805 return scnprintf(buf, PAGE_SIZE,
1806 "TSF sync is not initialized\n");
1807
1808 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink);
1809 if (!hdd_cm_is_vdev_associated(adapter->deflink) &&
1810 (adapter->device_mode == QDF_STA_MODE ||
1811 adapter->device_mode == QDF_P2P_CLIENT_MODE))
1812 return scnprintf(buf, PAGE_SIZE, "NOT connected\n");
1813
1814 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1815
1816 if (!hdd_ctx)
1817 return scnprintf(buf, PAGE_SIZE, "Invalid HDD context\n");
1818
1819 reg_qtime = qdf_get_log_timestamp();
1820 host_time = hdd_get_monotonic_host_time(hdd_ctx);
1821
1822 qtime = qdf_log_timestamp_to_usecs(reg_qtime);
1823 do_div(host_time, NSEC_PER_USEC);
1824 hdd_get_tsftime_from_qtime(adapter, qtime, &target_time);
1825 tsf_sync_qtime = adapter->tsf.last_tsf_sync_soc_time;
1826 do_div(tsf_sync_qtime, NSEC_PER_USEC);
1827
1828 if (adapter->device_mode == QDF_STA_MODE ||
1829 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
1830 mac = hdd_sta_ctx->conn_info.bssid.bytes;
1831 size = scnprintf(buf, PAGE_SIZE,
1832 "%s%llu %llu " QDF_MAC_ADDR_FMT
1833 " %llu %llu %llu\n",
1834 buf, adapter->tsf.last_target_time,
1835 tsf_sync_qtime,
1836 QDF_MAC_ADDR_REF(mac),
1837 qtime, host_time, target_time);
1838 } else {
1839 mac = adapter->mac_addr.bytes;
1840 size = scnprintf(buf, PAGE_SIZE,
1841 "%s%llu %llu " QDF_MAC_ADDR_FMT
1842 " %llu %llu %llu\n",
1843 buf, adapter->tsf.last_target_time,
1844 tsf_sync_qtime,
1845 QDF_MAC_ADDR_REF(mac),
1846 qtime, host_time, target_time);
1847 }
1848
1849 return size;
1850 }
1851
hdd_update_tsf(struct hdd_adapter * adapter,uint64_t tsf)1852 static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
1853 {
1854 struct hdd_tsf_op_response tsf_op_resp;
1855
1856 hdd_indicate_tsf_internal(adapter, &tsf_op_resp);
1857 hdd_update_timestamp(adapter);
1858 }
1859 #else
hdd_update_timestamp(struct hdd_adapter * adapter,uint64_t target_time,uint64_t host_time)1860 static void hdd_update_timestamp(struct hdd_adapter *adapter,
1861 uint64_t target_time, uint64_t host_time)
1862 {
1863 int interval = 0;
1864 enum hdd_ts_status sync_status;
1865 struct hdd_vdev_tsf *tsf;
1866 if (!adapter)
1867 return;
1868 tsf = &adapter->tsf;
1869 /* host time is updated in IRQ context, it's always before target time,
1870 * and so no need to try update last_host_time at present;
1871 * since the interval of capturing TSF
1872 * (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC) is long enough, host and target
1873 * time are updated in pairs, and one by one, we can return here to
1874 * avoid requiring spin lock, and to speed up the IRQ processing.
1875 */
1876 if (host_time > 0)
1877 tsf->cur_host_time = host_time;
1878
1879 qdf_spin_lock_bh(&tsf->host_target_sync_lock);
1880 if (target_time > 0)
1881 tsf->cur_target_time = target_time;
1882
1883 sync_status =
1884 hdd_check_timestamp_status(tsf->last_target_time,
1885 tsf->last_host_time,
1886 tsf->cur_target_time,
1887 tsf->cur_host_time,
1888 tsf->host_target_sync_force);
1889 if (tsf->host_target_sync_force)
1890 tsf->host_target_sync_force = false;
1891
1892 hdd_debug("sync_status %d", sync_status);
1893 switch (sync_status) {
1894 case HDD_TS_STATUS_INVALID:
1895 if (++tsf->continuous_error_count <
1896 MAX_CONTINUOUS_ERROR_CNT) {
1897 interval =
1898 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS;
1899 tsf->cur_target_time = 0;
1900 tsf->cur_host_time = 0;
1901 break;
1902 }
1903 hdd_warn("Reach the max continuous error count");
1904 /*
1905 * fall through:
1906 * If reach MAX_CONTINUOUS_ERROR_CNT, treat it as a
1907 * valid pair
1908 */
1909 case HDD_TS_STATUS_READY:
1910 tsf->last_target_time = tsf->cur_target_time;
1911 tsf->last_host_time = tsf->cur_host_time;
1912 tsf->cur_target_time = 0;
1913 tsf->cur_host_time = 0;
1914 hdd_debug("ts-pair updated: target: %llu; host: %llu",
1915 tsf->last_target_time,
1916 tsf->last_host_time);
1917
1918 /*
1919 * TSF-HOST need to be updated in at most
1920 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, it couldn't be achieved
1921 * if the timer interval is also
1922 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, due to processing or
1923 * schedule delay. So deduct several seconds from
1924 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC.
1925 * Without this change, hdd_get_hosttime_from_targettime() will
1926 * get wrong host time when it's longer than
1927 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC from last
1928 * TSF-HOST update.
1929 */
1930 interval = (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC -
1931 CAP_TSF_TIMER_FIX_SEC) * MSEC_PER_SEC;
1932 if (adapter->device_mode == QDF_SAP_MODE ||
1933 adapter->device_mode == QDF_P2P_GO_MODE) {
1934 interval *= WLAN_HDD_SOFTAP_INTERVAL_TIMES;
1935 }
1936
1937 tsf->continuous_error_count = 0;
1938 tsf->continuous_cap_retry_count = 0;
1939 hdd_debug("ts-pair updated: interval: %d",
1940 interval);
1941 break;
1942 case HDD_TS_STATUS_WAITING:
1943 interval = 0;
1944 hdd_warn("TS status is waiting due to one or more pair not updated");
1945
1946 if (!target_time && !host_time)
1947 interval = hdd_wlan_retry_tsf_cap(adapter);
1948 break;
1949 }
1950 qdf_spin_unlock_bh(&tsf->host_target_sync_lock);
1951
1952 if (interval > 0)
1953 qdf_mc_timer_start(&tsf->host_target_sync_timer,
1954 interval);
1955 }
1956
__hdd_wlan_tsf_show(struct device * dev,struct device_attribute * attr,char * buf)1957 static ssize_t __hdd_wlan_tsf_show(struct device *dev,
1958 struct device_attribute *attr, char *buf)
1959 {
1960 struct hdd_station_ctx *hdd_sta_ctx;
1961 struct hdd_adapter *adapter;
1962 struct hdd_context *hdd_ctx;
1963 ssize_t size;
1964 uint64_t host_time, target_time;
1965 uint8_t *mac;
1966
1967 struct net_device *net_dev = container_of(dev, struct net_device, dev);
1968
1969 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
1970 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)
1971 return scnprintf(buf, PAGE_SIZE, "Invalid device\n");
1972
1973 if (!hdd_get_th_sync_status(adapter))
1974 return scnprintf(buf, PAGE_SIZE,
1975 "TSF sync is not initialized\n");
1976
1977 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink);
1978 if (!hdd_cm_is_vdev_associated(adapter->deflink) &&
1979 (adapter->device_mode == QDF_STA_MODE ||
1980 adapter->device_mode == QDF_P2P_CLIENT_MODE))
1981 return scnprintf(buf, PAGE_SIZE, "NOT connected\n");
1982
1983 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
1984 if (!hdd_ctx)
1985 return scnprintf(buf, PAGE_SIZE, "Invalid HDD context\n");
1986
1987 host_time = hdd_get_monotonic_host_time(hdd_ctx);
1988
1989 if (hdd_get_targettime_from_hosttime(adapter, host_time,
1990 &target_time)) {
1991 size = scnprintf(buf, PAGE_SIZE, "Invalid timestamp\n");
1992 } else {
1993 if (adapter->device_mode == QDF_STA_MODE ||
1994 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
1995 mac = hdd_sta_ctx->conn_info.bssid.bytes;
1996 size = scnprintf(buf, PAGE_SIZE,
1997 "%s%llu %llu " QDF_MAC_ADDR_FMT "\n",
1998 buf, target_time, host_time,
1999 QDF_MAC_ADDR_REF(mac));
2000 } else {
2001 mac = adapter->mac_addr.bytes;
2002 size = scnprintf(buf, PAGE_SIZE,
2003 "%s%llu %llu " QDF_MAC_ADDR_FMT "\n",
2004 buf, target_time, host_time,
2005 QDF_MAC_ADDR_REF(mac));
2006 }
2007 }
2008
2009 return size;
2010 }
2011
hdd_update_tsf(struct hdd_adapter * adapter,uint64_t tsf)2012 static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
2013 {
2014 struct hdd_tsf_op_response tsf_op_resp;
2015
2016 hdd_indicate_tsf_internal(adapter, &tsf_op_resp);
2017 hdd_update_timestamp(adapter, tsf, 0);
2018 }
2019 #endif
2020
hdd_wlan_tsf_show(struct device * dev,struct device_attribute * attr,char * buf)2021 static ssize_t hdd_wlan_tsf_show(struct device *dev,
2022 struct device_attribute *attr, char *buf)
2023 {
2024 struct net_device *net_dev = container_of(dev, struct net_device, dev);
2025 struct osif_vdev_sync *vdev_sync;
2026 ssize_t err_size;
2027
2028 err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync);
2029 if (err_size)
2030 return err_size;
2031
2032 err_size = __hdd_wlan_tsf_show(dev, attr, buf);
2033
2034 osif_vdev_sync_op_stop(vdev_sync);
2035
2036 return err_size;
2037 }
2038
2039 static DEVICE_ATTR(tsf, 0444, hdd_wlan_tsf_show, NULL);
2040
hdd_tsf_sync_init(struct hdd_adapter * adapter)2041 static enum hdd_tsf_op_result hdd_tsf_sync_init(struct hdd_adapter *adapter)
2042 {
2043 QDF_STATUS ret;
2044 struct hdd_context *hddctx;
2045 struct net_device *net_dev;
2046 uint64_t host_time, qtime;
2047
2048 if (!adapter)
2049 return HDD_TSF_OP_FAIL;
2050
2051 hddctx = WLAN_HDD_GET_CTX(adapter);
2052 if (!hddctx) {
2053 hdd_err("invalid hdd context");
2054 return HDD_TSF_OP_FAIL;
2055 }
2056
2057 if (!qdf_atomic_read(&hddctx->tsf.tsf_ready_flag)) {
2058 hdd_err("TSF feature has NOT been initialized");
2059 return HDD_TSF_OP_FAIL;
2060 }
2061
2062 if (!adapter->tsf.enable_dynamic_tsf_sync) {
2063 hdd_debug("TSF sync feature not enabled");
2064 return HDD_TSF_OP_FAIL;
2065 }
2066
2067 if (hdd_get_th_sync_status(adapter)) {
2068 hdd_err("Host Target sync has been initialized!!");
2069 return HDD_TSF_OP_SUCC;
2070 }
2071
2072 qdf_spinlock_create(&adapter->tsf.host_target_sync_lock);
2073
2074 hdd_reset_timestamps(adapter);
2075
2076 ret = qdf_mc_timer_init(&adapter->tsf.host_target_sync_timer,
2077 QDF_TIMER_TYPE_SW,
2078 hdd_capture_tsf_timer_expired_handler,
2079 (void *)adapter);
2080 if (ret != QDF_STATUS_SUCCESS) {
2081 hdd_err("Failed to init target timer, ret: %d", ret);
2082 goto fail;
2083 }
2084
2085 ret = qdf_mc_timer_init(&adapter->tsf.host_capture_req_timer,
2086 QDF_TIMER_TYPE_SW,
2087 hdd_capture_req_timer_expired_handler,
2088 (void *)adapter);
2089 if (ret != QDF_STATUS_SUCCESS) {
2090 hdd_err("Failed to init capture timer, ret: %d", ret);
2091 qdf_mc_timer_destroy(&adapter->tsf.host_target_sync_timer);
2092 goto fail;
2093 }
2094
2095 hdd_tsf_regular_gpio_pulse_init(adapter);
2096
2097 net_dev = adapter->dev;
2098 if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx))
2099 device_create_file(&net_dev->dev, &dev_attr_tsf);
2100 hdd_set_th_sync_status(adapter, true);
2101
2102 qtime = qdf_get_log_timestamp();
2103 host_time = hdd_get_monotonic_host_time(hddctx);
2104
2105 qtime = qdf_log_timestamp_to_usecs(qtime);
2106 do_div(host_time, NSEC_PER_USEC);
2107
2108 adapter->delta_qtime = (qtime - host_time) * NSEC_PER_USEC;
2109
2110 return HDD_TSF_OP_SUCC;
2111 fail:
2112 hdd_set_th_sync_status(adapter, false);
2113 return HDD_TSF_OP_FAIL;
2114 }
2115
hdd_tsf_sync_deinit(struct hdd_adapter * adapter)2116 static enum hdd_tsf_op_result hdd_tsf_sync_deinit(struct hdd_adapter *adapter)
2117 {
2118 QDF_STATUS ret;
2119 struct hdd_context *hddctx;
2120 struct net_device *net_dev;
2121
2122 if (!adapter)
2123 return HDD_TSF_OP_FAIL;
2124
2125 if (!hdd_get_th_sync_status(adapter))
2126 return HDD_TSF_OP_SUCC;
2127
2128 hdd_set_th_sync_status(adapter, false);
2129
2130 hdd_tsf_regular_gpio_pulse_deinit(adapter);
2131
2132 ret = qdf_mc_timer_destroy(&adapter->tsf.host_target_sync_timer);
2133 if (ret != QDF_STATUS_SUCCESS)
2134 hdd_err("Failed to destroy target timer, ret: %d", ret);
2135
2136 ret = qdf_mc_timer_destroy(&adapter->tsf.host_capture_req_timer);
2137 if (ret != QDF_STATUS_SUCCESS)
2138 hdd_err("Failed to destroy capture timer, ret: %d", ret);
2139
2140 hddctx = WLAN_HDD_GET_CTX(adapter);
2141
2142 /* reset the cap_tsf flag and gpio if needed */
2143 if (hddctx && qdf_atomic_read(&hddctx->tsf.cap_tsf_flag) &&
2144 hddctx->tsf.cap_tsf_context == adapter) {
2145 int reset_ret = hdd_tsf_reset_gpio(adapter);
2146
2147 if (reset_ret)
2148 hdd_err("Failed to reset tsf gpio, ret:%d",
2149 reset_ret);
2150 hddctx->tsf.cap_tsf_context = NULL;
2151 qdf_atomic_set(&hddctx->tsf.cap_tsf_flag, 0);
2152 }
2153
2154 hdd_reset_timestamps(adapter);
2155
2156 net_dev = adapter->dev;
2157 if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx)) {
2158 struct device *dev = &net_dev->dev;
2159
2160 device_remove_file(dev, &dev_attr_tsf);
2161 }
2162 return HDD_TSF_OP_SUCC;
2163 }
2164
hdd_start_tsf_sync(struct hdd_adapter * adapter)2165 int hdd_start_tsf_sync(struct hdd_adapter *adapter)
2166 {
2167 enum hdd_tsf_op_result ret;
2168
2169 if (!adapter)
2170 return -EINVAL;
2171
2172 ret = hdd_tsf_sync_init(adapter);
2173 if (ret != HDD_TSF_OP_SUCC)
2174 return -EINVAL;
2175
2176 return (__hdd_start_tsf_sync(adapter) ==
2177 HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
2178 }
2179
hdd_restart_tsf_sync_post_wlan_resume(struct hdd_adapter * adapter)2180 void hdd_restart_tsf_sync_post_wlan_resume(struct hdd_adapter *adapter)
2181 {
2182 QDF_STATUS status;
2183 qdf_mc_timer_t *sync_timer;
2184
2185 if (!hdd_get_th_sync_status(adapter)) {
2186 hdd_err("Host TSF sync is not initialized!!");
2187 return;
2188 }
2189
2190 sync_timer = &adapter->tsf.host_target_sync_timer;
2191 if (QDF_TIMER_STATE_RUNNING ==
2192 qdf_mc_timer_get_current_state(sync_timer)) {
2193 status = qdf_mc_timer_stop_sync(sync_timer);
2194 if (status != QDF_STATUS_SUCCESS) {
2195 hdd_err("Couldn't stop Host TSF sync running timer!!");
2196 return;
2197 }
2198
2199 adapter->tsf.host_target_sync_force = true;
2200 status = qdf_mc_timer_start(sync_timer, 10);
2201 if (status != QDF_STATUS_SUCCESS)
2202 hdd_err("Host TSF sync timer restart failed");
2203
2204 hdd_debug("Host TSF sync timer restarted post wlan resume");
2205 }
2206 }
2207
hdd_stop_tsf_sync(struct hdd_adapter * adapter)2208 int hdd_stop_tsf_sync(struct hdd_adapter *adapter)
2209 {
2210 enum hdd_tsf_op_result ret;
2211
2212 if (!adapter)
2213 return -EINVAL;
2214
2215 ret = __hdd_stop_tsf_sync(adapter);
2216 if (ret != HDD_TSF_OP_SUCC)
2217 return -EINVAL;
2218
2219 ret = hdd_tsf_sync_deinit(adapter);
2220 if (ret != HDD_TSF_OP_SUCC) {
2221 hdd_err("Failed to deinit tsf sync, ret: %d", ret);
2222 return -EINVAL;
2223 }
2224 return 0;
2225 }
2226
__hdd_capture_tsf(struct hdd_adapter * adapter,uint32_t * buf,int len)2227 static inline int __hdd_capture_tsf(struct hdd_adapter *adapter,
2228 uint32_t *buf, int len)
2229 {
2230 if (!adapter || !buf) {
2231 hdd_err("invalid pointer");
2232 return -EINVAL;
2233 }
2234
2235 if (len != 1)
2236 return -EINVAL;
2237
2238 buf[0] = TSF_DISABLED_BY_TSFPLUS;
2239
2240 return 0;
2241 }
2242
2243 /**
2244 * hdd_handle_tsf_dynamic_start()
2245 * @adapter: Adapter pointer
2246 * @attr: TSF sync interval from NL interface
2247 *
2248 * This function enables TSF sync if capture mode is Dynamic set from ini
2249 *
2250 * Return: 0 for success or non-zero negative failure code
2251 */
hdd_handle_tsf_dynamic_start(struct hdd_adapter * adapter,struct nlattr * attr)2252 static int hdd_handle_tsf_dynamic_start(struct hdd_adapter *adapter,
2253 struct nlattr *attr)
2254 {
2255 struct hdd_context *hdd_ctx;
2256 struct hdd_vdev_tsf *tsf;
2257 uint32_t dynamic_tsf_sync_interval = 0;
2258
2259 if (!adapter)
2260 return -EINVAL;
2261
2262 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2263 if (wlan_hdd_validate_context(hdd_ctx))
2264 return -EINVAL;
2265
2266 if (attr)
2267 dynamic_tsf_sync_interval = nla_get_u32(attr);
2268
2269 tsf = &adapter->tsf;
2270
2271 if (tsf->enable_dynamic_tsf_sync) {
2272 if (dynamic_tsf_sync_interval ==
2273 tsf->dynamic_tsf_sync_interval) {
2274 return -EALREADY;
2275 }
2276 tsf->dynamic_tsf_sync_interval =
2277 dynamic_tsf_sync_interval;
2278 return 0;
2279 }
2280
2281 tsf->dynamic_tsf_sync_interval = dynamic_tsf_sync_interval;
2282 tsf->enable_dynamic_tsf_sync = true;
2283 if (hdd_tsf_is_time_sync_enabled_cfg(hdd_ctx))
2284 pld_set_tsf_sync_period(hdd_ctx->parent_dev,
2285 dynamic_tsf_sync_interval);
2286
2287 return hdd_start_tsf_sync(adapter);
2288 }
2289
2290 /**
2291 * hdd_handle_tsf_dynamic_stop()
2292 * @adapter: Adapter pointer
2293 *
2294 * This function disable TSF sync if capture mode is Dynamic set from ini
2295 *
2296 * Return: 0 for success or non-zero negative failure code
2297 */
hdd_handle_tsf_dynamic_stop(struct hdd_adapter * adapter)2298 static int hdd_handle_tsf_dynamic_stop(struct hdd_adapter *adapter)
2299 {
2300 struct hdd_context *hdd_ctx;
2301
2302 if (!adapter)
2303 return -EINVAL;
2304
2305 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2306 if (wlan_hdd_validate_context(hdd_ctx))
2307 return -EINVAL;
2308
2309 if (!adapter->tsf.enable_dynamic_tsf_sync)
2310 return -EALREADY;
2311
2312 adapter->tsf.enable_dynamic_tsf_sync = false;
2313 adapter->tsf.dynamic_tsf_sync_interval = 0;
2314 if (hdd_tsf_is_time_sync_enabled_cfg(hdd_ctx))
2315 pld_reset_tsf_sync_period(hdd_ctx->parent_dev);
2316
2317 return hdd_stop_tsf_sync(adapter);
2318 }
2319
2320 #if defined(WLAN_FEATURE_TSF_TIMER_SYNC)
__hdd_indicate_tsf(struct hdd_adapter * adapter,struct hdd_tsf_op_response * tsf_op_resp)2321 static enum hdd_tsf_op_result __hdd_indicate_tsf(struct hdd_adapter *adapter,
2322 struct hdd_tsf_op_response
2323 *tsf_op_resp)
2324 {
2325 if (!adapter || !tsf_op_resp) {
2326 hdd_err("invalid pointer");
2327 return HDD_TSF_OP_FAIL;
2328 }
2329
2330 memset(tsf_op_resp, 0, sizeof(*tsf_op_resp));
2331 if (!hdd_tsf_is_initialized(adapter)) {
2332 tsf_op_resp->status = TSF_NOT_READY;
2333 return HDD_TSF_OP_SUCC;
2334 }
2335
2336 tsf_op_resp->status = hdd_tsf_check_conn_state(adapter);
2337 if (tsf_op_resp->status != TSF_RETURN)
2338 return HDD_TSF_OP_SUCC;
2339
2340 if (adapter->tsf.last_target_time == 0) {
2341 hdd_info("TSF value not received");
2342 tsf_op_resp->status = TSF_NOT_RETURNED_BY_FW;
2343 return HDD_TSF_OP_SUCC;
2344 }
2345
2346 tsf_op_resp->time = adapter->tsf.last_target_time;
2347 tsf_op_resp->soc_time = adapter->tsf.last_tsf_sync_soc_time;
2348
2349 return HDD_TSF_OP_SUCC;
2350 }
2351
2352 #else
__hdd_indicate_tsf(struct hdd_adapter * adapter,struct hdd_tsf_op_response * tsf_op_resp)2353 static enum hdd_tsf_op_result __hdd_indicate_tsf(struct hdd_adapter *adapter,
2354 struct hdd_tsf_op_response
2355 *tsf_op_resp)
2356 {
2357 if (!adapter || !tsf_op_resp) {
2358 hdd_err("invalid pointer");
2359 return HDD_TSF_OP_FAIL;
2360 }
2361
2362 tsf_op_resp->status = TSF_DISABLED_BY_TSFPLUS;
2363 tsf_op_resp->time = 0;
2364 tsf_op_resp->soc_time = 0;
2365
2366 return HDD_TSF_OP_SUCC;
2367 }
2368 #endif
2369
2370 #ifdef WLAN_FEATURE_TSF_PLUS_SOCK_TS
2371 #ifdef CONFIG_HL_SUPPORT
2372 static inline
hdd_netbuf_timestamp(qdf_nbuf_t netbuf,uint64_t target_time)2373 enum hdd_tsf_op_result hdd_netbuf_timestamp(qdf_nbuf_t netbuf,
2374 uint64_t target_time)
2375 {
2376 struct hdd_adapter *adapter;
2377 struct net_device *net_dev = netbuf->dev;
2378
2379 if (!net_dev)
2380 return HDD_TSF_OP_FAIL;
2381
2382 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
2383 if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC &&
2384 hdd_get_th_sync_status(adapter)) {
2385 uint64_t host_time;
2386 int32_t ret = hdd_get_hosttime_from_targettime(adapter,
2387 target_time, &host_time);
2388 if (!ret) {
2389 netbuf->tstamp = ns_to_ktime(host_time);
2390 return HDD_TSF_OP_SUCC;
2391 }
2392 }
2393
2394 return HDD_TSF_OP_FAIL;
2395 }
2396
2397 #else
2398 static inline
hdd_netbuf_timestamp(qdf_nbuf_t netbuf,uint64_t target_time)2399 enum hdd_tsf_op_result hdd_netbuf_timestamp(qdf_nbuf_t netbuf,
2400 uint64_t target_time)
2401 {
2402 struct hdd_adapter *adapter;
2403 struct net_device *net_dev = netbuf->dev;
2404 struct skb_shared_hwtstamps hwtstamps;
2405
2406 if (!net_dev)
2407 return HDD_TSF_OP_FAIL;
2408
2409 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
2410 if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC &&
2411 hdd_get_th_sync_status(adapter)) {
2412 uint64_t tsf64_time = target_time;
2413 uint64_t soc_time = 0;/*ns*/
2414 int32_t ret = hdd_get_soctime_from_tsf64time(adapter,
2415 tsf64_time, &soc_time);
2416 if (!ret) {
2417 /* Adjust delta_qtime to soc_time(Qtime), so that
2418 * System Monotonic time and Qtime are in sync.
2419 */
2420 if (soc_time > (adapter->delta_qtime)) {
2421 hwtstamps.hwtstamp =
2422 soc_time - (adapter->delta_qtime);
2423 *skb_hwtstamps(netbuf) = hwtstamps;
2424 netbuf->tstamp = ktime_set(0, 0);
2425 return HDD_TSF_OP_SUCC;
2426 } else {
2427 return HDD_TSF_OP_FAIL;
2428 }
2429 }
2430 }
2431
2432 return HDD_TSF_OP_FAIL;
2433 }
2434 #endif
2435
2436 /**
2437 * hdd_tx_timestamp() - time stamp TX netbuf
2438 * @status: TX status
2439 * @netbuf: pointer to a TX netbuf
2440 * @target_time: TX time for the netbuf
2441 *
2442 * This function get corresponding host time from target time,
2443 * and time stamp the TX netbuf with this time
2444 *
2445 * Return: Describe the execute result of this routine
2446 */
hdd_tx_timestamp(enum htt_tx_status status,qdf_nbuf_t netbuf,uint64_t target_time)2447 static int hdd_tx_timestamp(enum htt_tx_status status,
2448 qdf_nbuf_t netbuf, uint64_t target_time)
2449 {
2450 struct sock *sk = netbuf->sk;
2451
2452 if (!sk)
2453 return -EINVAL;
2454
2455 if ((skb_shinfo(netbuf)->tx_flags & SKBTX_HW_TSTAMP) &&
2456 !(skb_shinfo(netbuf)->tx_flags & SKBTX_IN_PROGRESS)) {
2457 struct sock_exterr_skb *serr;
2458 qdf_nbuf_t new_netbuf;
2459 int err;
2460
2461 if (hdd_netbuf_timestamp(netbuf, target_time) !=
2462 HDD_TSF_OP_SUCC)
2463 return -EINVAL;
2464
2465 new_netbuf = qdf_nbuf_clone(netbuf);
2466 if (!new_netbuf)
2467 return -ENOMEM;
2468
2469 serr = SKB_EXT_ERR(new_netbuf);
2470 memset(serr, 0, sizeof(*serr));
2471
2472 switch (status) {
2473 case htt_tx_status_ok:
2474 serr->ee.ee_errno = ENOMSG;
2475 break;
2476 case htt_tx_status_discard:
2477 serr->ee.ee_errno = ENOBUFS;
2478 break;
2479 case htt_tx_status_no_ack:
2480 serr->ee.ee_errno = EREMOTEIO;
2481 break;
2482 default:
2483 serr->ee.ee_errno = ENOMSG;
2484 break;
2485 }
2486
2487 hdd_debug("packet status %d, sock ee_errno %d",
2488 status, serr->ee.ee_errno);
2489
2490 serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
2491
2492 err = sock_queue_err_skb(sk, new_netbuf);
2493 if (err) {
2494 qdf_nbuf_free(new_netbuf);
2495 return err;
2496 }
2497
2498 return 0;
2499 }
2500 return -EINVAL;
2501 }
2502
hdd_rx_timestamp(qdf_nbuf_t netbuf,uint64_t target_time)2503 int hdd_rx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time)
2504 {
2505 if (hdd_netbuf_timestamp(netbuf, target_time) ==
2506 HDD_TSF_OP_SUCC)
2507 return 0;
2508
2509 /* reset tstamp when failed */
2510 netbuf->tstamp = ktime_set(0, 0);
2511 return -EINVAL;
2512 }
2513
wlan_hdd_tsf_plus_sock_ts_init(struct hdd_context * hdd_ctx)2514 static inline void wlan_hdd_tsf_plus_sock_ts_init(struct hdd_context *hdd_ctx)
2515 {
2516 if (hdd_tsf_is_tx_set(hdd_ctx))
2517 ol_register_timestamp_callback(hdd_tx_timestamp);
2518 }
2519
wlan_hdd_tsf_plus_sock_ts_deinit(struct hdd_context * hdd_ctx)2520 static inline void wlan_hdd_tsf_plus_sock_ts_deinit(struct hdd_context *hdd_ctx)
2521 {
2522 if (hdd_tsf_is_tx_set(hdd_ctx))
2523 ol_deregister_timestamp_callback();
2524 }
2525 #else
wlan_hdd_tsf_plus_sock_ts_init(struct hdd_context * hdd_ctx)2526 static inline void wlan_hdd_tsf_plus_sock_ts_init(struct hdd_context *hdd_ctx)
2527 {
2528 }
2529
wlan_hdd_tsf_plus_sock_ts_deinit(struct hdd_context * hdd_ctx)2530 static inline void wlan_hdd_tsf_plus_sock_ts_deinit(struct hdd_context *hdd_ctx)
2531 {
2532 }
2533 #endif /* WLAN_FEATURE_TSF_PLUS_SOCK_TS */
2534
2535 #if defined(WLAN_FEATURE_TSF_PLUS_NOIRQ)
2536 static inline
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2537 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2538 {
2539
2540 wlan_hdd_tsf_plus_sock_ts_init(hdd_ctx);
2541 return HDD_TSF_OP_SUCC;
2542 }
2543
2544 static inline
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2545 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2546 {
2547 wlan_hdd_tsf_plus_sock_ts_deinit(hdd_ctx);
2548 return HDD_TSF_OP_SUCC;
2549 }
2550
2551 #elif defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_SYNC)
2552 static
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2553 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2554 {
2555 int ret;
2556 QDF_STATUS status;
2557 uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID;
2558
2559 status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc,
2560 &tsf_sync_gpio_pin);
2561 if (QDF_IS_STATUS_ERROR(status)) {
2562 hdd_err("tsf gpio irq host pin error");
2563 goto fail;
2564 }
2565
2566 if (tsf_sync_gpio_pin == TSF_GPIO_PIN_INVALID) {
2567 hdd_err("gpio host pin is invalid");
2568 goto fail;
2569 }
2570
2571 ret = gpio_request(tsf_sync_gpio_pin, "wlan_tsf");
2572 if (ret) {
2573 hdd_err("gpio host pin is invalid");
2574 goto fail;
2575 }
2576
2577 ret = gpio_direction_output(tsf_sync_gpio_pin, OUTPUT_LOW);
2578 if (ret) {
2579 hdd_err("gpio host pin is invalid");
2580 goto fail_free_gpio;
2581 }
2582
2583 wlan_hdd_tsf_plus_sock_ts_init(hdd_ctx);
2584
2585 return HDD_TSF_OP_SUCC;
2586
2587 fail_free_gpio:
2588 gpio_free(tsf_sync_gpio_pin);
2589 fail:
2590 return HDD_TSF_OP_FAIL;
2591 }
2592
2593 static
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2594 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2595 {
2596 QDF_STATUS status;
2597 uint32_t tsf_sync_gpio_pin = TSF_GPIO_PIN_INVALID;
2598
2599 status = ucfg_fwol_get_tsf_sync_host_gpio_pin(hdd_ctx->psoc,
2600 &tsf_sync_gpio_pin);
2601 if (QDF_IS_STATUS_ERROR(status))
2602 return QDF_STATUS_E_INVAL;
2603
2604 if (tsf_sync_gpio_pin == TSF_GPIO_PIN_INVALID)
2605 return QDF_STATUS_E_INVAL;
2606
2607 wlan_hdd_tsf_plus_sock_ts_deinit(hdd_ctx);
2608
2609 gpio_free(tsf_sync_gpio_pin);
2610 return HDD_TSF_OP_SUCC;
2611 }
2612
2613 #elif defined(WLAN_FEATURE_TSF_PLUS_EXT_GPIO_IRQ)
2614 static
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2615 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2616 {
2617 int ret;
2618 QDF_STATUS status;
2619 uint32_t tsf_irq_gpio_pin = TSF_GPIO_PIN_INVALID;
2620
2621 status = ucfg_fwol_get_tsf_irq_host_gpio_pin(hdd_ctx->psoc,
2622 &tsf_irq_gpio_pin);
2623 if (QDF_IS_STATUS_ERROR(status)) {
2624 hdd_err("tsf gpio irq host pin error");
2625 goto fail;
2626 }
2627
2628 if (tsf_irq_gpio_pin == TSF_GPIO_PIN_INVALID) {
2629 hdd_err("gpio host pin is invalid");
2630 goto fail;
2631 }
2632
2633 ret = gpio_request(tsf_irq_gpio_pin, "wlan_tsf");
2634 if (ret) {
2635 hdd_err("gpio host pin is invalid");
2636 goto fail;
2637 }
2638
2639 ret = gpio_direction_input(tsf_irq_gpio_pin);
2640 if (ret) {
2641 hdd_err("gpio host pin is invalid");
2642 goto fail_free_gpio;
2643 }
2644
2645 tsf_gpio_irq_num = gpio_to_irq(tsf_irq_gpio_pin);
2646 if (tsf_gpio_irq_num < 0) {
2647 hdd_err("fail to get irq: %d", tsf_gpio_irq_num);
2648 goto fail_free_gpio;
2649 }
2650
2651 ret = request_irq(tsf_gpio_irq_num, hdd_tsf_captured_irq_handler,
2652 IRQF_SHARED | IRQF_TRIGGER_RISING, "wlan_tsf",
2653 hdd_ctx);
2654
2655 if (ret) {
2656 hdd_err("Failed to register irq handler: %d", ret);
2657 goto fail_free_gpio;
2658 }
2659
2660 wlan_hdd_tsf_plus_sock_ts_init(hdd_ctx);
2661
2662 return HDD_TSF_OP_SUCC;
2663
2664 fail_free_gpio:
2665 gpio_free(tsf_irq_gpio_pin);
2666 fail:
2667 tsf_gpio_irq_num = -1;
2668 return HDD_TSF_OP_FAIL;
2669 }
2670
2671 static
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2672 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2673 {
2674 QDF_STATUS status;
2675 uint32_t tsf_irq_gpio_pin = TSF_GPIO_PIN_INVALID;
2676
2677 status = ucfg_fwol_get_tsf_irq_host_gpio_pin(hdd_ctx->psoc,
2678 &tsf_irq_gpio_pin);
2679 if (QDF_IS_STATUS_ERROR(status))
2680 return QDF_STATUS_E_INVAL;
2681
2682 if (tsf_irq_gpio_pin == TSF_GPIO_PIN_INVALID)
2683 return QDF_STATUS_E_INVAL;
2684
2685 wlan_hdd_tsf_plus_sock_ts_deinit(hdd_ctx);
2686
2687 if (tsf_gpio_irq_num >= 0) {
2688 free_irq(tsf_gpio_irq_num, hdd_ctx);
2689 tsf_gpio_irq_num = -1;
2690 gpio_free(tsf_irq_gpio_pin);
2691 }
2692
2693 return HDD_TSF_OP_SUCC;
2694 }
2695
2696 #elif defined(WLAN_FEATURE_TSF_TIMER_SYNC)
2697 static inline
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2698 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2699 {
2700 return HDD_TSF_OP_SUCC;
2701 }
2702
2703 static inline
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2704 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2705 {
2706 return HDD_TSF_OP_SUCC;
2707 }
2708 #else
2709 static inline
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2710 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2711 {
2712 int ret;
2713
2714 ret = cnss_common_register_tsf_captured_handler(
2715 hdd_ctx->parent_dev,
2716 hdd_tsf_captured_irq_handler,
2717 (void *)hdd_ctx);
2718 if (ret != 0) {
2719 hdd_err("Failed to register irq handler: %d", ret);
2720 return HDD_TSF_OP_FAIL;
2721 }
2722
2723 wlan_hdd_tsf_plus_sock_ts_init(hdd_ctx);
2724 return HDD_TSF_OP_SUCC;
2725 }
2726
2727 static inline
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2728 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2729 {
2730 int ret;
2731
2732 wlan_hdd_tsf_plus_sock_ts_deinit(hdd_ctx);
2733
2734 ret = cnss_common_unregister_tsf_captured_handler(
2735 hdd_ctx->parent_dev,
2736 (void *)hdd_ctx);
2737 if (ret != 0) {
2738 hdd_err("Failed to unregister irq handler, ret:%d",
2739 ret);
2740 ret = HDD_TSF_OP_FAIL;
2741 }
2742
2743 return HDD_TSF_OP_SUCC;
2744 }
2745 #endif
2746 #else
hdd_update_tsf(struct hdd_adapter * adapter,uint64_t tsf)2747 static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
2748 {
2749 }
2750
__hdd_indicate_tsf(struct hdd_adapter * adapter,struct hdd_tsf_op_response * tsf_op_resp)2751 static enum hdd_tsf_op_result __hdd_indicate_tsf(struct hdd_adapter *adapter,
2752 struct hdd_tsf_op_response
2753 *tsf_op_resp)
2754 {
2755 return hdd_indicate_tsf_internal(adapter, tsf_op_resp);
2756 }
2757
__hdd_capture_tsf(struct hdd_adapter * adapter,uint32_t * buf,int len)2758 static inline int __hdd_capture_tsf(struct hdd_adapter *adapter,
2759 uint32_t *buf, int len)
2760 {
2761 return (hdd_capture_tsf_internal(adapter, buf, len) ==
2762 HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
2763 }
2764
2765 static inline
wlan_hdd_tsf_plus_init(struct hdd_context * hdd_ctx)2766 enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
2767 {
2768 return HDD_TSF_OP_SUCC;
2769 }
2770
2771 static inline
wlan_hdd_tsf_plus_deinit(struct hdd_context * hdd_ctx)2772 enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
2773 {
2774 return HDD_TSF_OP_SUCC;
2775 }
2776
hdd_handle_tsf_dynamic_start(struct hdd_adapter * adapter,struct nlattr * attr)2777 static inline int hdd_handle_tsf_dynamic_start(struct hdd_adapter *adapter,
2778 struct nlattr *attr)
2779 {
2780 return -ENOTSUPP;
2781 }
2782
hdd_handle_tsf_dynamic_stop(struct hdd_adapter * adapter)2783 static inline int hdd_handle_tsf_dynamic_stop(struct hdd_adapter *adapter)
2784 {
2785 return -ENOTSUPP;
2786 }
2787 #endif /* WLAN_FEATURE_TSF_PLUS */
2788
hdd_capture_tsf(struct hdd_adapter * adapter,uint32_t * buf,int len)2789 int hdd_capture_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len)
2790 {
2791 return __hdd_capture_tsf(adapter, buf, len);
2792 }
2793
hdd_indicate_tsf(struct hdd_adapter * adapter,struct hdd_tsf_op_response * tsf_op_resp)2794 int hdd_indicate_tsf(struct hdd_adapter *adapter,
2795 struct hdd_tsf_op_response *tsf_op_resp)
2796 {
2797 if (__hdd_indicate_tsf(adapter, tsf_op_resp) == HDD_TSF_OP_FAIL)
2798 return -EINVAL;
2799
2800 switch (tsf_op_resp->status) {
2801 case TSF_RETURN:
2802 return 0;
2803 case TSF_NOT_RETURNED_BY_FW:
2804 return -EINPROGRESS;
2805 case TSF_STA_NOT_CONNECTED_NO_TSF:
2806 case TSF_SAP_NOT_STARTED_NO_TSF:
2807 return -EPERM;
2808 default:
2809 return -EINVAL;
2810 }
2811 }
2812
2813 #ifdef WLAN_FEATURE_TSF_PTP
wlan_get_ts_info(struct net_device * dev,struct ethtool_ts_info * info)2814 int wlan_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
2815
2816 {
2817 struct hdd_adapter *adapter = netdev_priv(dev);
2818 struct osif_vdev_sync *vdev_sync;
2819 struct hdd_context *hdd_ctx;
2820 int errno;
2821
2822 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
2823 errno = wlan_hdd_validate_context(hdd_ctx);
2824 if (errno)
2825 return -EINVAL;
2826
2827 errno = osif_vdev_sync_op_start(dev, &vdev_sync);
2828 if (errno)
2829 return -EAGAIN;
2830
2831 info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
2832 SOF_TIMESTAMPING_RX_HARDWARE |
2833 SOF_TIMESTAMPING_RAW_HARDWARE;
2834 if (hdd_ctx->tsf.ptp_clock)
2835 info->phc_index = ptp_clock_index(hdd_ctx->tsf.ptp_clock);
2836 else
2837 info->phc_index = -1;
2838
2839 osif_vdev_sync_op_stop(vdev_sync);
2840 return 0;
2841 }
2842
2843 #if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE)
2844 /**
2845 * wlan_ptp_gettime() - return fw ts info to uplayer
2846 * @ptp: pointer to ptp_clock_info.
2847 * @ts: pointer to timespec.
2848 *
2849 * Return: Describe the execute result of this routine
2850 */
wlan_ptp_gettime(struct ptp_clock_info * ptp,struct timespec * ts)2851 static int wlan_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
2852 {
2853 uint64_t host_time, target_time = 0;
2854 struct hdd_context *hdd_ctx;
2855 struct hdd_adapter *adapter;
2856 struct osif_psoc_sync *psoc_sync;
2857 int errno, status = 0;
2858
2859 hdd_ctx = = cds_get_context(QDF_MODULE_ID_HDD);
2860 errno = wlan_hdd_validate_context(hdd_ctx);
2861 if (errno)
2862 return -EINVAL;
2863
2864 errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync);
2865 if (errno)
2866 return -EAGAIN;
2867
2868 adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_GO_MODE);
2869 if (!adapter) {
2870 adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_CLIENT_MODE);
2871 if (!adapter) {
2872 adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
2873 if (!adapter)
2874 adapter = hdd_get_adapter(hdd_ctx,
2875 QDF_STA_MODE);
2876 if (!adapter) {
2877 status = -EOPNOTSUPP;
2878 goto end;
2879 }
2880 }
2881 }
2882
2883 host_time = hdd_get_monotonic_host_time(hdd_ctx);
2884 if (hdd_get_targettime_from_hosttime(adapter, host_time,
2885 &target_time)) {
2886 hdd_err("get invalid target timestamp");
2887 status = -EINVAL;
2888 goto end;
2889 }
2890 *ts = ns_to_timespec(target_time * NSEC_PER_USEC);
2891
2892 end:
2893 osif_psoc_sync_op_stop(psoc_sync);
2894 return status;
2895 }
2896
2897 /**
2898 * wlan_hdd_phc_init() - phc init
2899 * @hdd_ctx: pointer to the hdd_context.
2900 *
2901 * Return: NULL
2902 */
wlan_hdd_phc_init(struct hdd_context * hdd_ctx)2903 static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx)
2904 {
2905 hdd_ctx->tsf.ptp_cinfo.gettime = wlan_ptp_gettime;
2906
2907 hdd_ctx->tsf.ptp_clock = ptp_clock_register(&hdd_ctx->tsf.ptp_cinfo,
2908 hdd_ctx->parent_dev);
2909 }
2910
2911 /**
2912 * wlan_hdd_phc_deinit() - phc deinit
2913 * @hdd_ctx: pointer to the hdd_context.
2914 *
2915 * Return: NULL
2916 */
wlan_hdd_phc_deinit(struct hdd_context * hdd_ctx)2917 static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx)
2918 {
2919 hdd_ctx->tsf.ptp_cinfo.gettime = NULL;
2920
2921 if (hdd_ctx->tsf.ptp_clock) {
2922 ptp_clock_unregister(hdd_ctx->tsf.ptp_clock);
2923 hdd_ctx->tsf.ptp_clock = NULL;
2924 }
2925 }
2926
2927 #else
wlan_ptp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)2928 static int wlan_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
2929 {
2930 uint64_t host_time, target_time = 0;
2931 struct hdd_context *hdd_ctx;
2932 struct hdd_adapter *adapter;
2933 struct osif_psoc_sync *psoc_sync;
2934 int errno, status = 0;
2935
2936 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
2937 errno = wlan_hdd_validate_context(hdd_ctx);
2938 if (errno)
2939 return -EINVAL;
2940
2941 errno = osif_psoc_sync_op_start(hdd_ctx->parent_dev, &psoc_sync);
2942 if (errno)
2943 return -EAGAIN;
2944
2945 adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_GO_MODE);
2946 if (!adapter) {
2947 adapter = hdd_get_adapter(hdd_ctx, QDF_P2P_CLIENT_MODE);
2948 if (!adapter) {
2949 adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
2950 if (!adapter)
2951 adapter = hdd_get_adapter(hdd_ctx,
2952 QDF_STA_MODE);
2953 if (!adapter) {
2954 status = -EOPNOTSUPP;
2955 goto end;
2956 }
2957 }
2958 }
2959
2960 host_time = hdd_get_monotonic_host_time(hdd_ctx);
2961 if (hdd_get_targettime_from_hosttime(adapter, host_time,
2962 &target_time)) {
2963 hdd_err("get invalid target timestamp");
2964 status = -EINVAL;
2965 goto end;
2966 }
2967 *ts = ns_to_timespec64(target_time * NSEC_PER_USEC);
2968
2969 end:
2970 osif_psoc_sync_op_stop(psoc_sync);
2971 return status;
2972 }
2973
wlan_hdd_phc_init(struct hdd_context * hdd_ctx)2974 static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx)
2975 {
2976 hdd_ctx->tsf.ptp_cinfo.gettime64 = wlan_ptp_gettime;
2977 hdd_ctx->tsf.ptp_clock = ptp_clock_register(&hdd_ctx->tsf.ptp_cinfo,
2978 hdd_ctx->parent_dev);
2979 }
2980
wlan_hdd_phc_deinit(struct hdd_context * hdd_ctx)2981 static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx)
2982 {
2983 hdd_ctx->tsf.ptp_cinfo.gettime64 = NULL;
2984
2985 if (hdd_ctx->tsf.ptp_clock) {
2986 ptp_clock_unregister(hdd_ctx->tsf.ptp_clock);
2987 hdd_ctx->tsf.ptp_clock = NULL;
2988 }
2989 }
2990
2991 #endif
2992 #else
2993
wlan_hdd_phc_init(struct hdd_context * hdd_ctx)2994 static void wlan_hdd_phc_init(struct hdd_context *hdd_ctx)
2995 {
2996 }
2997
wlan_hdd_phc_deinit(struct hdd_context * hdd_ctx)2998 static void wlan_hdd_phc_deinit(struct hdd_context *hdd_ctx)
2999 {
3000 }
3001 #endif /* WLAN_FEATURE_TSF_PTP */
3002
3003 #ifdef WLAN_FEATURE_TSF_AUTO_REPORT
hdd_tsf_auto_report_init(struct hdd_adapter * adapter)3004 void hdd_tsf_auto_report_init(struct hdd_adapter *adapter)
3005 {
3006 adapter->tsf.auto_rpt_src = 0;
3007 }
3008
3009 int
hdd_set_tsf_auto_report(struct hdd_adapter * adapter,bool ena,enum hdd_tsf_auto_rpt_source source)3010 hdd_set_tsf_auto_report(struct hdd_adapter *adapter, bool ena,
3011 enum hdd_tsf_auto_rpt_source source)
3012 {
3013 int ret = 0;
3014 bool enabled;
3015
3016 enabled = !!adapter->tsf.auto_rpt_src;
3017 if (enabled == ena) {
3018 hdd_debug_rl("source %d current %d and no action is required",
3019 source, enabled);
3020 goto set_src;
3021 }
3022
3023 ret = wma_cli_set_command((int)adapter->deflink->vdev_id,
3024 ena ? (int)GEN_PARAM_TSF_AUTO_REPORT_ENABLE :
3025 (int)GEN_PARAM_TSF_AUTO_REPORT_DISABLE,
3026 ena, GEN_CMD);
3027 if (ret) {
3028 hdd_err_rl("source %d enable %d failed: %d", source, ena, ret);
3029 ret = -EINPROGRESS;
3030 goto out;
3031 }
3032
3033 set_src:
3034 if (ena)
3035 qdf_atomic_set_bit(source, &adapter->tsf.auto_rpt_src);
3036 else
3037 qdf_atomic_clear_bit(source, &adapter->tsf.auto_rpt_src);
3038
3039 out:
3040 return ret;
3041 }
3042
3043 /**
3044 * hdd_tsf_auto_report_enabled() - get current state of tsf auto report
3045 * for an adapter
3046 * @adapter: pointer to Adapter context
3047 *
3048 * Return: true for enabled, false for disabled
3049 */
hdd_tsf_auto_report_enabled(struct hdd_adapter * adapter)3050 static inline bool hdd_tsf_auto_report_enabled(struct hdd_adapter *adapter)
3051 {
3052 return !!adapter->tsf.auto_rpt_src;
3053 }
3054
3055 /**
3056 * hdd_set_delta_tsf() - calculate and save the time difference between
3057 * TQM clock and TSF clock
3058 * @adapter: pointer to Adapter context
3059 * @ptsf: pointer to tsf information
3060 *
3061 * Return: QDF_STATUS
3062 */
hdd_set_delta_tsf(struct hdd_adapter * adapter,struct stsf * ptsf)3063 static QDF_STATUS hdd_set_delta_tsf(struct hdd_adapter *adapter,
3064 struct stsf *ptsf)
3065 {
3066 void *soc = cds_get_context(QDF_MODULE_ID_SOC);
3067 uint32_t delta_tsf;
3068
3069 /* A tsf report event with mac_id_valid equals to 1 represents
3070 * for tsf auto report, that's what we need to handle in this
3071 * function; otherwise, return failure here so that legacy BSS
3072 * TSF logic can be continued.
3073 */
3074 if (!ptsf->mac_id_valid) {
3075 hdd_debug_rl("Not TSF auto report");
3076 return QDF_STATUS_E_FAILURE;
3077 }
3078
3079 if (!hdd_tsf_auto_report_enabled(adapter)) {
3080 hdd_debug_rl("adapter %u tsf_auto_report disabled",
3081 adapter->deflink->vdev_id);
3082 goto exit_with_success;
3083 }
3084
3085 delta_tsf = ptsf->tsf_low - ptsf->soc_timer_low;
3086 hdd_debug("vdev %u tsf_low %u qtimer_low %u delta_tsf %u",
3087 ptsf->vdev_id, ptsf->tsf_low, ptsf->soc_timer_low, delta_tsf);
3088
3089 /* Pass delta_tsf to DP layer to calculate hw delay
3090 * on a per vdev basis
3091 */
3092 cdp_set_delta_tsf(soc, adapter->deflink->vdev_id, delta_tsf);
3093
3094 exit_with_success:
3095 return QDF_STATUS_SUCCESS;
3096 }
3097 #else /* !WLAN_FEATURE_TSF_AUTO_REPORT */
hdd_set_delta_tsf(struct hdd_adapter * adapter,struct stsf * ptsf)3098 static inline QDF_STATUS hdd_set_delta_tsf(struct hdd_adapter *adapter,
3099 struct stsf *ptsf)
3100 {
3101 return QDF_STATUS_E_NOSUPPORT;
3102 }
3103
hdd_tsf_auto_report_enabled(struct hdd_adapter * adapter)3104 static inline bool hdd_tsf_auto_report_enabled(struct hdd_adapter *adapter)
3105 {
3106 return false;
3107 }
3108 #endif /* WLAN_FEATURE_TSF_AUTO_REPORT */
3109
3110 #ifdef WLAN_FEATURE_TSF_UPLINK_DELAY
3111 /**
3112 * hdd_set_tsf_ul_delay_report() - enable or disable tsf uplink delay report
3113 * for an adapter
3114 * @adapter: pointer to Adapter context
3115 * @ena: requesting state (true or false)
3116 *
3117 * Return: 0 for success or non-zero negative failure code
3118 */
3119 static int
hdd_set_tsf_ul_delay_report(struct hdd_adapter * adapter,bool ena)3120 hdd_set_tsf_ul_delay_report(struct hdd_adapter *adapter, bool ena)
3121 {
3122 void *soc = cds_get_context(QDF_MODULE_ID_SOC);
3123 QDF_STATUS status;
3124
3125 status = cdp_set_tsf_ul_delay_report(soc,
3126 adapter->deflink->vdev_id,
3127 ena);
3128
3129 if (QDF_IS_STATUS_ERROR(status)) {
3130 hdd_err_rl("Set tsf report uplink delay failed");
3131 return -EPERM;
3132 }
3133
3134 return 0;
3135 }
3136
hdd_get_uplink_delay_len(struct hdd_adapter * adapter)3137 uint32_t hdd_get_uplink_delay_len(struct hdd_adapter *adapter)
3138 {
3139 if (adapter->device_mode != QDF_STA_MODE)
3140 return 0;
3141
3142 return nla_total_size(sizeof(uint32_t));
3143 }
3144
hdd_add_uplink_delay(struct hdd_adapter * adapter,struct sk_buff * skb)3145 QDF_STATUS hdd_add_uplink_delay(struct hdd_adapter *adapter,
3146 struct sk_buff *skb)
3147 {
3148 void *soc = cds_get_context(QDF_MODULE_ID_SOC);
3149 QDF_STATUS status;
3150 uint32_t ul_delay;
3151
3152 if (adapter->device_mode != QDF_STA_MODE &&
3153 adapter->device_mode != QDF_P2P_CLIENT_MODE)
3154 return QDF_STATUS_SUCCESS;
3155
3156 if (hdd_tsf_auto_report_enabled(adapter)) {
3157 status = cdp_get_uplink_delay(soc, adapter->deflink->vdev_id,
3158 &ul_delay);
3159 if (QDF_IS_STATUS_ERROR(status))
3160 ul_delay = 0;
3161 } else {
3162 ul_delay = 0;
3163 }
3164
3165 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY,
3166 ul_delay))
3167 return QDF_STATUS_E_FAILURE;
3168
3169 return QDF_STATUS_SUCCESS;
3170 }
3171 #else
3172 static inline int
hdd_set_tsf_ul_delay_report(struct hdd_adapter * adapter,bool ena)3173 hdd_set_tsf_ul_delay_report(struct hdd_adapter *adapter, bool ena)
3174 {
3175 return -ENOTSUPP;
3176 }
3177 #endif /* WLAN_FEATURE_TSF_UPLINK_DELAY */
3178
3179 /**
3180 * hdd_get_tsf_cb() - handle tsf callback
3181 * @pcb_cxt: pointer to the hdd_contex
3182 * @ptsf: pointer to struct stsf
3183 *
3184 * This function handle the event that reported by firmware at first.
3185 * The event contains the vdev_id, current tsf value of this vdev,
3186 * tsf value is 64bits, discripted in two variable tsf_low and tsf_high.
3187 * These two values each is uint32.
3188 *
3189 * Return: 0 for success or non-zero negative failure code
3190 */
hdd_get_tsf_cb(void * pcb_cxt,struct stsf * ptsf)3191 int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf)
3192 {
3193 struct hdd_context *hddctx;
3194 struct hdd_adapter *adapter;
3195 struct wlan_hdd_link_info *link_info;
3196 int ret;
3197 uint64_t tsf_sync_soc_time;
3198 QDF_TIMER_STATE capture_req_timer_status;
3199 qdf_mc_timer_t *capture_timer;
3200 struct hdd_vdev_tsf *tsf;
3201
3202 if (!pcb_cxt || !ptsf) {
3203 hdd_err("HDD context is not valid");
3204 return -EINVAL;
3205 }
3206
3207 hddctx = (struct hdd_context *)pcb_cxt;
3208 ret = wlan_hdd_validate_context(hddctx);
3209 if (0 != ret)
3210 return -EINVAL;
3211
3212 link_info = hdd_get_link_info_by_vdev(hddctx, ptsf->vdev_id);
3213 if (!link_info) {
3214 hdd_err("failed to find adapter");
3215 return -EINVAL;
3216 }
3217
3218 adapter = link_info->adapter;
3219 /* Intercept tsf report and check if it is for auto report.
3220 * If yes, return in advance and skip the legacy BSS TSF
3221 * report. Otherwise continue on to the legacy BSS TSF
3222 * report logic.
3223 */
3224 if (QDF_IS_STATUS_SUCCESS(hdd_set_delta_tsf(adapter, ptsf)))
3225 return 0;
3226
3227 if (!hdd_tsf_is_initialized(adapter)) {
3228 hdd_err("tsf is not init, ignore tsf event");
3229 return -EINVAL;
3230 }
3231
3232 hdd_debug("tsf cb handle event, device_mode is %d",
3233 adapter->device_mode);
3234
3235 wlan_hdd_tsf_reg_update_details(adapter, ptsf);
3236
3237 tsf = &adapter->tsf;
3238 capture_timer = &tsf->host_capture_req_timer;
3239 capture_req_timer_status =
3240 qdf_mc_timer_get_current_state(capture_timer);
3241 if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) {
3242 hdd_warn("invalid timer status");
3243 return -EINVAL;
3244 }
3245
3246 qdf_mc_timer_stop(capture_timer);
3247 tsf->cur_target_time = ((uint64_t)ptsf->tsf_high << 32 |
3248 ptsf->tsf_low);
3249
3250 tsf->cur_target_global_tsf_time =
3251 ((uint64_t)ptsf->global_tsf_high << 32 |
3252 ptsf->global_tsf_low);
3253 tsf_sync_soc_time = ((uint64_t)ptsf->soc_timer_high << 32 |
3254 ptsf->soc_timer_low);
3255 tsf->cur_tsf_sync_soc_time =
3256 hdd_convert_qtime_to_us(tsf_sync_soc_time) * NSEC_PER_USEC;
3257
3258 qdf_event_set(&tsf_sync_get_completion_evt);
3259 hdd_update_tsf(adapter, tsf->cur_target_time);
3260 hdd_debug("Vdev=%u, tsf_low=%u, tsf_high=%u ptsf->soc_timer_low=%u ptsf->soc_timer_high=%u",
3261 ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high,
3262 ptsf->soc_timer_low, ptsf->soc_timer_high);
3263 return 0;
3264 }
3265
3266 const struct nla_policy tsf_policy[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1] = {
3267 [QCA_WLAN_VENDOR_ATTR_TSF_CMD] = {.type = NLA_U32},
3268 [QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL] = {.type = NLA_U32},
3269 };
3270
3271 /**
3272 * __wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
3273 * @wiphy: Pointer to wireless phy
3274 * @wdev: Pointer to wireless device
3275 * @data: Pointer to data
3276 * @data_len: Data length
3277 *
3278 * Handle TSF SET / GET operation from userspace
3279 *
3280 * Return: 0 on success, negative errno on failure
3281 */
__wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)3282 static int __wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
3283 struct wireless_dev *wdev,
3284 const void *data,
3285 int data_len)
3286 {
3287 struct net_device *dev = wdev->netdev;
3288 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
3289 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
3290 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1];
3291 struct hdd_tsf_op_response tsf_op_resp;
3292 struct nlattr *attr;
3293 enum hdd_tsf_get_state value;
3294 int status;
3295 QDF_STATUS ret;
3296 struct sk_buff *reply_skb;
3297 uint32_t tsf_cmd;
3298 enum qca_nl80211_vendor_subcmds_index index =
3299 QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX;
3300 bool enable_auto_rpt;
3301 enum hdd_tsf_auto_rpt_source source =
3302 HDD_TSF_AUTO_RPT_SOURCE_UPLINK_DELAY;
3303
3304 hdd_enter_dev(wdev->netdev);
3305
3306 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
3307 hdd_err("Command not allowed in FTM mode");
3308 return -EPERM;
3309 }
3310
3311 status = wlan_hdd_validate_context(hdd_ctx);
3312 if (0 != status)
3313 return -EINVAL;
3314
3315 if (wlan_cfg80211_nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TSF_MAX,
3316 data, data_len, tsf_policy)) {
3317 hdd_err("Invalid TSF cmd");
3318 return -EINVAL;
3319 }
3320
3321 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]) {
3322 hdd_err("Invalid TSF cmd");
3323 return -EINVAL;
3324 }
3325 tsf_cmd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]);
3326
3327 /* Intercept tsf_cmd for TSF auto report enable or disable subcmds,
3328 * and treat as trigger for uplink delay report.
3329 */
3330 if (tsf_cmd == QCA_TSF_AUTO_REPORT_DISABLE ||
3331 tsf_cmd == QCA_TSF_AUTO_REPORT_ENABLE) {
3332 enable_auto_rpt = (tsf_cmd == QCA_TSF_AUTO_REPORT_ENABLE);
3333 status = hdd_set_tsf_auto_report(adapter,
3334 enable_auto_rpt,
3335 source);
3336 if (status)
3337 goto end;
3338
3339 hdd_set_tsf_ul_delay_report(adapter, enable_auto_rpt);
3340 goto end;
3341 }
3342
3343 ret = qdf_event_reset(&tsf_sync_get_completion_evt);
3344 if (QDF_IS_STATUS_ERROR(ret))
3345 hdd_warn("failed to reset tsf_sync_get_completion_evt");
3346
3347 if (tsf_cmd == QCA_TSF_CAPTURE || tsf_cmd == QCA_TSF_SYNC_GET) {
3348 hdd_capture_tsf(adapter, &value, 1);
3349 switch (value) {
3350 case TSF_RETURN:
3351 status = 0;
3352 break;
3353 case TSF_CURRENT_IN_CAP_STATE:
3354 status = -EALREADY;
3355 break;
3356 case TSF_STA_NOT_CONNECTED_NO_TSF:
3357 case TSF_SAP_NOT_STARTED_NO_TSF:
3358 status = -EPERM;
3359 break;
3360 default:
3361 case TSF_CAPTURE_FAIL:
3362 status = -EINVAL;
3363 break;
3364 }
3365 } else if (tsf_cmd == QCA_TSF_SYNC_START) {
3366 attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL];
3367 status = hdd_handle_tsf_dynamic_start(adapter, attr);
3368 } else if (tsf_cmd == QCA_TSF_SYNC_STOP) {
3369 status = hdd_handle_tsf_dynamic_stop(adapter);
3370 } else {
3371 status = 0;
3372 }
3373
3374 if (status < 0)
3375 goto end;
3376
3377 if (tsf_cmd == QCA_TSF_SYNC_GET) {
3378 ret = qdf_wait_single_event(&tsf_sync_get_completion_evt,
3379 WLAN_TSF_SYNC_GET_TIMEOUT);
3380 if (QDF_IS_STATUS_ERROR(ret)) {
3381 status = -ETIMEDOUT;
3382 goto end;
3383 }
3384 }
3385
3386 if (tsf_cmd == QCA_TSF_GET || tsf_cmd == QCA_TSF_SYNC_GET) {
3387 status = hdd_indicate_tsf(adapter, &tsf_op_resp);
3388 if (status != 0)
3389 goto end;
3390
3391 reply_skb =
3392 wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
3393 sizeof(uint64_t) * 2 +
3394 NLMSG_HDRLEN,
3395 index, GFP_KERNEL);
3396 if (!reply_skb) {
3397 hdd_err("wlan_cfg80211_vendor_cmd_alloc_reply_skb failed");
3398 status = -ENOMEM;
3399 goto end;
3400 }
3401 if (hdd_wlan_nla_put_u64(reply_skb,
3402 QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
3403 tsf_op_resp.time) ||
3404 hdd_wlan_nla_put_u64(reply_skb,
3405 QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
3406 tsf_op_resp.soc_time)) {
3407 hdd_err("nla put fail");
3408 wlan_cfg80211_vendor_free_skb(reply_skb);
3409 status = -EINVAL;
3410 goto end;
3411 }
3412 status = wlan_cfg80211_vendor_cmd_reply(reply_skb);
3413 }
3414
3415 end:
3416 hdd_info("TSF operation %d status: %d", tsf_cmd, status);
3417 return status;
3418 }
3419
wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)3420 int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
3421 struct wireless_dev *wdev,
3422 const void *data,
3423 int data_len)
3424 {
3425 int errno;
3426 struct osif_vdev_sync *vdev_sync;
3427
3428 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
3429 if (errno)
3430 return errno;
3431
3432 errno = __wlan_hdd_cfg80211_handle_tsf_cmd(wiphy, wdev, data, data_len);
3433
3434 osif_vdev_sync_op_stop(vdev_sync);
3435
3436 return errno;
3437 }
3438
3439 /**
3440 * wlan_hdd_tsf_init() - set callback to handle tsf value.
3441 * @hdd_ctx: pointer to the struct hdd_context
3442 *
3443 * This function set the callback to sme module, the callback will be
3444 * called when a tsf event is reported by firmware
3445 *
3446 * Return: none
3447 */
wlan_hdd_tsf_init(struct hdd_context * hdd_ctx)3448 void wlan_hdd_tsf_init(struct hdd_context *hdd_ctx)
3449 {
3450 QDF_STATUS status;
3451
3452 if (!hdd_ctx)
3453 return;
3454
3455 if (qdf_atomic_inc_return(&hdd_ctx->tsf.tsf_ready_flag) > 1) {
3456 hdd_err("TSF ready flag already set");
3457 return;
3458 }
3459
3460 qdf_atomic_init(&hdd_ctx->tsf.cap_tsf_flag);
3461
3462 status = qdf_event_create(&tsf_sync_get_completion_evt);
3463 if (QDF_IS_STATUS_ERROR(status)) {
3464 hdd_err("failed to create tsf_sync_get_completion_evt");
3465 goto fail;
3466 }
3467
3468 status = hdd_tsf_set_gpio(hdd_ctx);
3469
3470 if (QDF_STATUS_SUCCESS != status) {
3471 hdd_err("set tsf GPIO failed, status: %d", status);
3472 goto fail;
3473 }
3474
3475 if (wlan_hdd_tsf_plus_init(hdd_ctx) != HDD_TSF_OP_SUCC) {
3476 hdd_err("TSF plus init failed");
3477 goto fail;
3478 }
3479
3480 if (hdd_tsf_is_ptp_enabled(hdd_ctx))
3481 wlan_hdd_phc_init(hdd_ctx);
3482
3483 return;
3484
3485 fail:
3486 qdf_atomic_set(&hdd_ctx->tsf.tsf_ready_flag, 0);
3487 }
3488
wlan_hdd_tsf_deinit(struct hdd_context * hdd_ctx)3489 void wlan_hdd_tsf_deinit(struct hdd_context *hdd_ctx)
3490 {
3491 QDF_STATUS status;
3492
3493 if (!hdd_ctx)
3494 return;
3495
3496 status = qdf_event_destroy(&tsf_sync_get_completion_evt);
3497 if (QDF_IS_STATUS_ERROR(status))
3498 hdd_err("failed to destroy tsf_sync_get_completion_evt");
3499
3500 if (!qdf_atomic_read(&hdd_ctx->tsf.tsf_ready_flag)) {
3501 hdd_err("ready flag not set");
3502 return;
3503 }
3504
3505 if (hdd_tsf_is_ptp_enabled(hdd_ctx))
3506 wlan_hdd_phc_deinit(hdd_ctx);
3507 wlan_hdd_tsf_plus_deinit(hdd_ctx);
3508 qdf_atomic_set(&hdd_ctx->tsf.tsf_ready_flag, 0);
3509 qdf_atomic_set(&hdd_ctx->tsf.cap_tsf_flag, 0);
3510 }
3511