xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_mc_timer.c (revision 503663c6daafffe652fa360bde17243568cd6d2a)
1 /*
2  * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: qdf_mc_timer
21  * QCA driver framework timer APIs serialized to MC thread
22  */
23 
24 /* Include Files */
25 #include <qdf_debug_domain.h>
26 #include <qdf_mc_timer.h>
27 #include <qdf_lock.h>
28 #include "qdf_lock.h"
29 #include "qdf_list.h"
30 #include "qdf_mem.h"
31 #include <qdf_module.h>
32 #include "qdf_timer.h"
33 #include <linux/time64.h>
34 
35 /* Preprocessor definitions and constants */
36 #define LINUX_TIMER_COOKIE 0x12341234
37 #define LINUX_INVALID_TIMER_COOKIE 0xfeedface
38 #define TMR_INVALID_ID (0)
39 
40 static uint32_t g_qdf_timer_multiplier = 1;
41 
42 inline void qdf_timer_set_multiplier(uint32_t multiplier)
43 {
44 	g_qdf_timer_multiplier = multiplier;
45 }
46 qdf_export_symbol(qdf_timer_set_multiplier);
47 
48 inline uint32_t qdf_timer_get_multiplier(void)
49 {
50 	return g_qdf_timer_multiplier;
51 }
52 qdf_export_symbol(qdf_timer_get_multiplier);
53 
54 /* Type declarations */
55 
56 /* Static Variable Definitions */
57 static unsigned int persistent_timer_count;
58 static qdf_mutex_t persistent_timer_count_lock;
59 
60 static void (*scheduler_timer_callback)(qdf_mc_timer_t *);
61 void qdf_register_mc_timer_callback(void (*callback) (qdf_mc_timer_t *))
62 {
63 	scheduler_timer_callback = callback;
64 }
65 
66 qdf_export_symbol(qdf_register_mc_timer_callback);
67 
68 /* Function declarations and documenation */
69 
70 /**
71  * qdf_try_allowing_sleep() - clean up timer states after it has been deactivated
72  * @type: timer type
73  *
74  * Clean up timer states after it has been deactivated check and try to allow
75  * sleep after a timer has been stopped or expired.
76  *
77  * Return: none
78  */
79 void qdf_try_allowing_sleep(QDF_TIMER_TYPE type)
80 {
81 	if (QDF_TIMER_TYPE_WAKE_APPS == type) {
82 
83 		persistent_timer_count--;
84 		if (0 == persistent_timer_count) {
85 			/* since the number of persistent timers has
86 			 * decreased from 1 to 0, the timer should allow
87 			 * sleep
88 			 */
89 		}
90 	}
91 }
92 qdf_export_symbol(qdf_try_allowing_sleep);
93 
94 /**
95  * qdf_mc_timer_get_current_state() - get the current state of the timer
96  * @timer: Pointer to timer object
97  *
98  * Return:
99  * QDF_TIMER_STATE - qdf timer state
100  */
101 QDF_TIMER_STATE qdf_mc_timer_get_current_state(qdf_mc_timer_t *timer)
102 {
103 	if (!timer) {
104 		QDF_ASSERT(0);
105 		return QDF_TIMER_STATE_UNUSED;
106 	}
107 
108 	switch (timer->state) {
109 	case QDF_TIMER_STATE_STOPPED:
110 	case QDF_TIMER_STATE_STARTING:
111 	case QDF_TIMER_STATE_RUNNING:
112 	case QDF_TIMER_STATE_UNUSED:
113 		return timer->state;
114 	default:
115 		QDF_ASSERT(0);
116 		return QDF_TIMER_STATE_UNUSED;
117 	}
118 }
119 qdf_export_symbol(qdf_mc_timer_get_current_state);
120 
121 /**
122  * qdf_timer_module_init() - initializes a QDF timer module.
123  *
124  * This API initializes the QDF timer module. This needs to be called
125  * exactly once prior to using any QDF timers.
126  *
127  * Return: none
128  */
129 void qdf_timer_module_init(void)
130 {
131 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
132 		  "Initializing the QDF MC timer module");
133 	qdf_mutex_create(&persistent_timer_count_lock);
134 }
135 qdf_export_symbol(qdf_timer_module_init);
136 
137 #ifdef TIMER_MANAGER
138 
139 static qdf_list_t qdf_timer_domains[QDF_DEBUG_DOMAIN_COUNT];
140 static qdf_spinlock_t qdf_timer_list_lock;
141 
142 static inline qdf_list_t *qdf_timer_list_get(enum qdf_debug_domain domain)
143 {
144 	return &qdf_timer_domains[domain];
145 }
146 
147 /**
148  * qdf_mc_timer_manager_init() - initialize QDF debug timer manager
149  *
150  * This API initializes QDF timer debug functionality.
151  *
152  * Return: none
153  */
154 void qdf_mc_timer_manager_init(void)
155 {
156 	int i;
157 
158 	for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
159 		qdf_list_create(&qdf_timer_domains[i], 1000);
160 	qdf_spinlock_create(&qdf_timer_list_lock);
161 }
162 qdf_export_symbol(qdf_mc_timer_manager_init);
163 
164 static void qdf_mc_timer_print_list(qdf_list_t *timers)
165 {
166 	QDF_STATUS status;
167 	qdf_list_node_t *node;
168 
169 	qdf_spin_lock_irqsave(&qdf_timer_list_lock);
170 	status = qdf_list_peek_front(timers, &node);
171 	while (QDF_IS_STATUS_SUCCESS(status)) {
172 		qdf_mc_timer_node_t *timer_node = (qdf_mc_timer_node_t *)node;
173 		const char *filename = kbasename(timer_node->file_name);
174 		uint32_t line = timer_node->line_num;
175 
176 		qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
177 		qdf_err("timer Leak@ File %s, @Line %u", filename, line);
178 		qdf_spin_lock_irqsave(&qdf_timer_list_lock);
179 
180 		status = qdf_list_peek_next(timers, node, &node);
181 	}
182 	qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
183 }
184 
185 void qdf_mc_timer_check_for_leaks(void)
186 {
187 	enum qdf_debug_domain current_domain = qdf_debug_domain_get();
188 	qdf_list_t *timers = qdf_timer_list_get(current_domain);
189 
190 	if (qdf_list_empty(timers))
191 		return;
192 
193 	qdf_err("Timer leaks detected in %s domain!",
194 		qdf_debug_domain_name(current_domain));
195 	qdf_mc_timer_print_list(timers);
196 	QDF_DEBUG_PANIC("Previously reported timer leaks detected");
197 }
198 
199 static void qdf_mc_timer_free_leaked_timers(qdf_list_t *timers)
200 {
201 	QDF_STATUS status;
202 	qdf_list_node_t *node;
203 
204 	qdf_spin_lock_irqsave(&qdf_timer_list_lock);
205 	status = qdf_list_remove_front(timers, &node);
206 	while (QDF_IS_STATUS_SUCCESS(status)) {
207 		qdf_mem_free(node);
208 		status = qdf_list_remove_front(timers, &node);
209 	}
210 	qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
211 }
212 
213 /**
214  * qdf_timer_clean() - clean up QDF timer debug functionality
215  *
216  * This API cleans up QDF timer debug functionality and prints which QDF timers
217  * are leaked. This is called during driver unload.
218  *
219  * Return: none
220  */
221 static void qdf_timer_clean(void)
222 {
223 	bool leaks_detected = false;
224 	int i;
225 
226 	/* detect and print leaks */
227 	for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i) {
228 		qdf_list_t *timers = &qdf_timer_domains[i];
229 
230 		if (qdf_list_empty(timers))
231 			continue;
232 
233 		leaks_detected = true;
234 
235 		qdf_err("\nTimer leaks detected in the %s (Id %d) domain!",
236 			qdf_debug_domain_name(i), i);
237 		qdf_mc_timer_print_list(timers);
238 	}
239 
240 	/* we're done if there were no leaks */
241 	if (!leaks_detected)
242 		return;
243 
244 	/* panic, if enabled */
245 	QDF_DEBUG_PANIC("Previously reported timer leaks detected");
246 
247 	/* if we didn't crash, release the leaked timers */
248 	for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
249 		qdf_mc_timer_free_leaked_timers(&qdf_timer_domains[i]);
250 }
251 qdf_export_symbol(qdf_timer_clean);
252 
253 /**
254  * qdf_mc_timer_manager_exit() - exit QDF timer debug functionality
255  *
256  * This API exists QDF timer debug functionality
257  *
258  * Return: none
259  */
260 void qdf_mc_timer_manager_exit(void)
261 {
262 	int i;
263 
264 	qdf_timer_clean();
265 
266 	for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
267 		qdf_list_destroy(&qdf_timer_domains[i]);
268 
269 	qdf_spinlock_destroy(&qdf_timer_list_lock);
270 }
271 qdf_export_symbol(qdf_mc_timer_manager_exit);
272 #endif
273 
274 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
275 static void __os_mc_timer_shim(struct timer_list *os_timer)
276 {
277 	qdf_mc_timer_platform_t *platform_info_ptr =
278 				qdf_container_of(os_timer,
279 						 qdf_mc_timer_platform_t,
280 						 timer);
281 	qdf_mc_timer_t *timer = qdf_container_of(platform_info_ptr,
282 						 qdf_mc_timer_t,
283 						 platform_info);
284 
285 	scheduler_timer_callback(timer);
286 }
287 
288 static void qdf_mc_timer_setup(qdf_mc_timer_t *timer,
289 			       QDF_TIMER_TYPE timer_type)
290 {
291 	uint32_t flags = 0;
292 
293 	if (QDF_TIMER_TYPE_SW == timer_type)
294 		flags |= TIMER_DEFERRABLE;
295 
296 	timer_setup(&timer->platform_info.timer,
297 		    __os_mc_timer_shim, flags);
298 }
299 #else
300 static void __os_mc_timer_shim(unsigned long data)
301 {
302 	qdf_mc_timer_t *timer = (qdf_mc_timer_t *)data;
303 
304 	scheduler_timer_callback(timer);
305 }
306 
307 static void qdf_mc_timer_setup(qdf_mc_timer_t *timer,
308 			       QDF_TIMER_TYPE timer_type)
309 {
310 	if (QDF_TIMER_TYPE_SW == timer_type)
311 		init_timer_deferrable(&timer->platform_info.timer);
312 	else
313 		init_timer(&timer->platform_info.timer);
314 
315 	timer->platform_info.timer.function = __os_mc_timer_shim;
316 	timer->platform_info.timer.data = (unsigned long)timer;
317 }
318 #endif
319 /**
320  * qdf_mc_timer_init() - initialize a QDF timer
321  * @timer: Pointer to timer object
322  * @timer_type: Type of timer
323  * @callback: Callback to be called after timer expiry
324  * @ser_data: User data which will be passed to callback function
325  *
326  * This API initializes a QDF timer object.
327  *
328  * qdf_mc_timer_init() initializes a QDF timer object. A timer must be
329  * initialized by calling qdf_mc_timer_initialize() before it may be used in
330  * any other timer functions.
331  *
332  * Attempting to initialize timer that is already initialized results in
333  * a failure. A destroyed timer object can be re-initialized with a call to
334  * qdf_mc_timer_init(). The results of otherwise referencing the object
335  * after it has been destroyed are undefined.
336  *
337  *  Calls to QDF timer functions to manipulate the timer such
338  *  as qdf_mc_timer_set() will fail if the timer is not initialized or has
339  *  been destroyed. Therefore, don't use the timer after it has been
340  *  destroyed until it has been re-initialized.
341  *
342  *  All callback will be executed within the CDS main thread unless it is
343  *  initialized from the Tx thread flow, in which case it will be executed
344  *  within the tx thread flow.
345  *
346  * Return:
347  * QDF_STATUS_SUCCESS: timer is initialized successfully
348  * QDF failure status: timer initialization failed
349  */
350 #ifdef TIMER_MANAGER
351 QDF_STATUS qdf_mc_timer_init_debug(qdf_mc_timer_t *timer,
352 				   QDF_TIMER_TYPE timer_type,
353 				   qdf_mc_timer_callback_t callback,
354 				   void *user_data, char *file_name,
355 				   uint32_t line_num)
356 {
357 	enum qdf_debug_domain current_domain = qdf_debug_domain_get();
358 	qdf_list_t *active_timers = qdf_timer_list_get(current_domain);
359 	QDF_STATUS qdf_status;
360 
361 	/* check for invalid pointer */
362 	if ((!timer) || (!callback)) {
363 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
364 			  "%s: Null params being passed", __func__);
365 		QDF_ASSERT(0);
366 		return QDF_STATUS_E_FAULT;
367 	}
368 
369 	timer->timer_node = qdf_mem_malloc(sizeof(qdf_mc_timer_node_t));
370 
371 	if (!timer->timer_node) {
372 		QDF_ASSERT(0);
373 		return QDF_STATUS_E_NOMEM;
374 	}
375 
376 	timer->timer_node->file_name = file_name;
377 	timer->timer_node->line_num = line_num;
378 	timer->timer_node->qdf_timer = timer;
379 
380 	qdf_spin_lock_irqsave(&qdf_timer_list_lock);
381 	qdf_status = qdf_list_insert_front(active_timers,
382 					   &timer->timer_node->node);
383 	qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
384 	if (QDF_STATUS_SUCCESS != qdf_status) {
385 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
386 			  "%s: Unable to insert node into List qdf_status %d",
387 			  __func__, qdf_status);
388 	}
389 
390 	/* set the various members of the timer structure
391 	 * with arguments passed or with default values
392 	 */
393 	qdf_spinlock_create(&timer->platform_info.spinlock);
394 	qdf_mc_timer_setup(timer, timer_type);
395 	timer->callback = callback;
396 	timer->user_data = user_data;
397 	timer->type = timer_type;
398 	timer->platform_info.cookie = LINUX_TIMER_COOKIE;
399 	timer->platform_info.thread_id = 0;
400 	timer->state = QDF_TIMER_STATE_STOPPED;
401 
402 	return QDF_STATUS_SUCCESS;
403 }
404 qdf_export_symbol(qdf_mc_timer_init_debug);
405 #else
406 QDF_STATUS qdf_mc_timer_init(qdf_mc_timer_t *timer, QDF_TIMER_TYPE timer_type,
407 			     qdf_mc_timer_callback_t callback,
408 			     void *user_data)
409 {
410 	/* check for invalid pointer */
411 	if ((!timer) || (!callback)) {
412 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
413 			  "%s: Null params being passed", __func__);
414 		QDF_ASSERT(0);
415 		return QDF_STATUS_E_FAULT;
416 	}
417 
418 	/* set the various members of the timer structure
419 	 * with arguments passed or with default values
420 	 */
421 	qdf_spinlock_create(&timer->platform_info.spinlock);
422 	qdf_mc_timer_setup(timer, timer_type);
423 	timer->callback = callback;
424 	timer->user_data = user_data;
425 	timer->type = timer_type;
426 	timer->platform_info.cookie = LINUX_TIMER_COOKIE;
427 	timer->platform_info.thread_id = 0;
428 	timer->state = QDF_TIMER_STATE_STOPPED;
429 
430 	return QDF_STATUS_SUCCESS;
431 }
432 qdf_export_symbol(qdf_mc_timer_init);
433 #endif
434 
435 /**
436  * qdf_mc_timer_destroy() - destroy QDF timer
437  * @timer: Pointer to timer object
438  *
439  * qdf_mc_timer_destroy() function shall destroy the timer object.
440  * After a successful return from \a qdf_mc_timer_destroy() the timer
441  * object becomes, in effect, uninitialized.
442  *
443  * A destroyed timer object can be re-initialized by calling
444  * qdf_mc_timer_init().  The results of otherwise referencing the object
445  * after it has been destroyed are undefined.
446  *
447  * Calls to QDF timer functions to manipulate the timer, such
448  * as qdf_mc_timer_set() will fail if the lock is destroyed.  Therefore,
449  * don't use the timer after it has been destroyed until it has
450  * been re-initialized.
451  *
452  * Return:
453  * QDF_STATUS_SUCCESS - timer is initialized successfully
454  * QDF failure status - timer initialization failed
455  */
456 #ifdef TIMER_MANAGER
457 QDF_STATUS qdf_mc_timer_destroy(qdf_mc_timer_t *timer)
458 {
459 	enum qdf_debug_domain current_domain = qdf_debug_domain_get();
460 	qdf_list_t *active_timers = qdf_timer_list_get(current_domain);
461 	QDF_STATUS v_status = QDF_STATUS_SUCCESS;
462 
463 	/* check for invalid pointer */
464 	if (!timer) {
465 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
466 			  "%s: Null timer pointer being passed", __func__);
467 		QDF_ASSERT(0);
468 		return QDF_STATUS_E_FAULT;
469 	}
470 
471 	/* Check if timer refers to an uninitialized object */
472 	if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) {
473 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
474 			  "%s: Cannot destroy uninitialized timer", __func__);
475 		QDF_ASSERT(0);
476 		return QDF_STATUS_E_INVAL;
477 	}
478 
479 	qdf_spin_lock_irqsave(&qdf_timer_list_lock);
480 	v_status = qdf_list_remove_node(active_timers,
481 					&timer->timer_node->node);
482 	qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
483 	if (v_status != QDF_STATUS_SUCCESS) {
484 		QDF_ASSERT(0);
485 		return QDF_STATUS_E_INVAL;
486 	}
487 	qdf_mem_free(timer->timer_node);
488 
489 	qdf_spin_lock_irqsave(&timer->platform_info.spinlock);
490 
491 	switch (timer->state) {
492 
493 	case QDF_TIMER_STATE_STARTING:
494 		v_status = QDF_STATUS_E_BUSY;
495 		break;
496 
497 	case QDF_TIMER_STATE_RUNNING:
498 		/* Stop the timer first */
499 		del_timer(&(timer->platform_info.timer));
500 		v_status = QDF_STATUS_SUCCESS;
501 		break;
502 	case QDF_TIMER_STATE_STOPPED:
503 		v_status = QDF_STATUS_SUCCESS;
504 		break;
505 
506 	case QDF_TIMER_STATE_UNUSED:
507 		v_status = QDF_STATUS_E_ALREADY;
508 		break;
509 
510 	default:
511 		v_status = QDF_STATUS_E_FAULT;
512 		break;
513 	}
514 
515 	if (QDF_STATUS_SUCCESS == v_status) {
516 		timer->platform_info.cookie = LINUX_INVALID_TIMER_COOKIE;
517 		timer->state = QDF_TIMER_STATE_UNUSED;
518 		qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
519 		qdf_spinlock_destroy(&timer->platform_info.spinlock);
520 		return v_status;
521 	}
522 
523 	qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
524 
525 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
526 		  "%s: Cannot destroy timer in state = %d", __func__,
527 		  timer->state);
528 	QDF_ASSERT(0);
529 
530 	return v_status;
531 }
532 qdf_export_symbol(qdf_mc_timer_destroy);
533 
534 #else
535 
536 /**
537  * qdf_mc_timer_destroy() - destroy QDF timer
538  * @timer: Pointer to timer object
539  *
540  * qdf_mc_timer_destroy() function shall destroy the timer object.
541  * After a successful return from \a qdf_mc_timer_destroy() the timer
542  * object becomes, in effect, uninitialized.
543  *
544  * A destroyed timer object can be re-initialized by calling
545  * qdf_mc_timer_init(). The results of otherwise referencing the object
546  * after it has been destroyed are undefined.
547  *
548  * Calls to QDF timer functions to manipulate the timer, such
549  * as qdf_mc_timer_set() will fail if the lock is destroyed. Therefore,
550  * don't use the timer after it has been destroyed until it has
551  * been re-initialized.
552  *
553  * Return:
554  * QDF_STATUS_SUCCESS - timer is initialized successfully
555  * QDF failure status - timer initialization failed
556  */
557 QDF_STATUS qdf_mc_timer_destroy(qdf_mc_timer_t *timer)
558 {
559 	QDF_STATUS v_status = QDF_STATUS_SUCCESS;
560 
561 	/* check for invalid pointer */
562 	if (!timer) {
563 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
564 			  "%s: Null timer pointer being passed", __func__);
565 		QDF_ASSERT(0);
566 		return QDF_STATUS_E_FAULT;
567 	}
568 
569 	/* check if timer refers to an uninitialized object */
570 	if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) {
571 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
572 			  "%s: Cannot destroy uninitialized timer", __func__);
573 		QDF_ASSERT(0);
574 		return QDF_STATUS_E_INVAL;
575 	}
576 	qdf_spin_lock_irqsave(&timer->platform_info.spinlock);
577 
578 	switch (timer->state) {
579 
580 	case QDF_TIMER_STATE_STARTING:
581 		v_status = QDF_STATUS_E_BUSY;
582 		break;
583 
584 	case QDF_TIMER_STATE_RUNNING:
585 		/* Stop the timer first */
586 		del_timer(&(timer->platform_info.timer));
587 		v_status = QDF_STATUS_SUCCESS;
588 		break;
589 
590 	case QDF_TIMER_STATE_STOPPED:
591 		v_status = QDF_STATUS_SUCCESS;
592 		break;
593 
594 	case QDF_TIMER_STATE_UNUSED:
595 		v_status = QDF_STATUS_E_ALREADY;
596 		break;
597 
598 	default:
599 		v_status = QDF_STATUS_E_FAULT;
600 		break;
601 	}
602 
603 	if (QDF_STATUS_SUCCESS == v_status) {
604 		timer->platform_info.cookie = LINUX_INVALID_TIMER_COOKIE;
605 		timer->state = QDF_TIMER_STATE_UNUSED;
606 		qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
607 		return v_status;
608 	}
609 
610 	qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
611 
612 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
613 		  "%s: Cannot destroy timer in state = %d", __func__,
614 		  timer->state);
615 	QDF_ASSERT(0);
616 
617 	return v_status;
618 }
619 qdf_export_symbol(qdf_mc_timer_destroy);
620 #endif
621 
622 /**
623  * qdf_mc_timer_start() - start a QDF timer object
624  * @timer: Pointer to timer object
625  * @expiration_time: Time to expire
626  *
627  * qdf_mc_timer_start() function starts a timer to expire after the
628  * specified interval, thus running the timer callback function when
629  * the interval expires.
630  *
631  * A timer only runs once (a one-shot timer). To re-start the
632  * timer, qdf_mc_timer_start() has to be called after the timer runs
633  * or has been cancelled.
634  *
635  * Return:
636  * QDF_STATUS_SUCCESS: timer is initialized successfully
637  * QDF failure status: timer initialization failed
638  */
639 QDF_STATUS qdf_mc_timer_start(qdf_mc_timer_t *timer, uint32_t expiration_time)
640 {
641 	/* check for invalid pointer */
642 	if (!timer) {
643 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
644 			  "%s Null timer pointer being passed", __func__);
645 		QDF_ASSERT(0);
646 		return QDF_STATUS_E_INVAL;
647 	}
648 
649 	/* check if timer refers to an uninitialized object */
650 	if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) {
651 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
652 			  "%s: Cannot start uninitialized timer", __func__);
653 		QDF_ASSERT(0);
654 
655 		return QDF_STATUS_E_INVAL;
656 	}
657 
658 	/* check if timer has expiration time less than 10 ms */
659 	if (expiration_time < 10) {
660 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
661 			  "%s: Cannot start a timer with expiration less than 10 ms",
662 			  __func__);
663 		QDF_ASSERT(0);
664 		return QDF_STATUS_E_INVAL;
665 	}
666 
667 	/* make sure the remainer of the logic isn't interrupted */
668 	qdf_spin_lock_irqsave(&timer->platform_info.spinlock);
669 
670 	/* ensure if the timer can be started */
671 	if (QDF_TIMER_STATE_STOPPED != timer->state) {
672 		qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
673 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
674 			  "%s: Cannot start timer in state = %d %ps",
675 			  __func__, timer->state, (void *)timer->callback);
676 		return QDF_STATUS_E_ALREADY;
677 	}
678 
679 	/* start the timer */
680 	mod_timer(&(timer->platform_info.timer),
681 		  jiffies + __qdf_scaled_msecs_to_jiffies(expiration_time));
682 
683 	timer->state = QDF_TIMER_STATE_RUNNING;
684 
685 	/* get the thread ID on which the timer is being started */
686 	timer->platform_info.thread_id = current->pid;
687 
688 	if (QDF_TIMER_TYPE_WAKE_APPS == timer->type) {
689 		persistent_timer_count++;
690 		if (1 == persistent_timer_count) {
691 			/* since we now have one persistent timer,
692 			 * we need to disallow sleep
693 			 * sleep_negate_okts(sleep_client_handle);
694 			 */
695 		}
696 	}
697 
698 	qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
699 
700 	return QDF_STATUS_SUCCESS;
701 }
702 qdf_export_symbol(qdf_mc_timer_start);
703 
704 /**
705  * qdf_mc_timer_stop() - stop a QDF timer
706  * @timer: Pointer to timer object
707  * qdf_mc_timer_stop() function stops a timer that has been started but
708  * has not expired, essentially cancelling the 'start' request.
709  *
710  * After a timer is stopped, it goes back to the state it was in after it
711  * was created and can be started again via a call to qdf_mc_timer_start().
712  *
713  * Return:
714  * QDF_STATUS_SUCCESS: timer is initialized successfully
715  * QDF failure status: timer initialization failed
716  */
717 QDF_STATUS qdf_mc_timer_stop(qdf_mc_timer_t *timer)
718 {
719 	/* check for invalid pointer */
720 	if (!timer) {
721 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
722 			  "%s Null timer pointer being passed", __func__);
723 		QDF_ASSERT(0);
724 		return QDF_STATUS_E_INVAL;
725 	}
726 
727 	/* check if timer refers to an uninitialized object */
728 	if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) {
729 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
730 			  "%s: Cannot stop uninitialized timer", __func__);
731 		QDF_ASSERT(0);
732 
733 		return QDF_STATUS_E_INVAL;
734 	}
735 
736 	/* ensure the timer state is correct */
737 	qdf_spin_lock_irqsave(&timer->platform_info.spinlock);
738 
739 	if (QDF_TIMER_STATE_RUNNING != timer->state) {
740 		qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
741 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
742 			  "%s: Cannot stop timer in state = %d",
743 			  __func__, timer->state);
744 		return QDF_STATUS_SUCCESS;
745 	}
746 
747 	timer->state = QDF_TIMER_STATE_STOPPED;
748 
749 	qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
750 
751 	del_timer(&(timer->platform_info.timer));
752 
753 	qdf_try_allowing_sleep(timer->type);
754 
755 	return QDF_STATUS_SUCCESS;
756 }
757 qdf_export_symbol(qdf_mc_timer_stop);
758 
759 QDF_STATUS qdf_mc_timer_stop_sync(qdf_mc_timer_t *timer)
760 {
761 	/* check for invalid pointer */
762 	if (!timer) {
763 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
764 			  "%s Null timer pointer being passed", __func__);
765 		QDF_ASSERT(0);
766 		return QDF_STATUS_E_INVAL;
767 	}
768 
769 	/* check if timer refers to an uninitialized object */
770 	if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) {
771 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
772 			  "%s: Cannot stop uninitialized timer", __func__);
773 		QDF_ASSERT(0);
774 
775 		return QDF_STATUS_E_INVAL;
776 	}
777 
778 	/* ensure the timer state is correct */
779 	qdf_spin_lock_irqsave(&timer->platform_info.spinlock);
780 
781 	if (QDF_TIMER_STATE_RUNNING != timer->state) {
782 		qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
783 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
784 			  "%s: Cannot stop timer in state = %d",
785 			  __func__, timer->state);
786 		return QDF_STATUS_SUCCESS;
787 	}
788 
789 	timer->state = QDF_TIMER_STATE_STOPPED;
790 
791 	qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock);
792 	del_timer_sync(&(timer->platform_info.timer));
793 
794 	qdf_try_allowing_sleep(timer->type);
795 
796 	return QDF_STATUS_SUCCESS;
797 }
798 qdf_export_symbol(qdf_mc_timer_stop_sync);
799 /**
800  * qdf_mc_timer_get_system_ticks() - get the system time in 10ms ticks
801 
802  * qdf_mc_timer_get_system_ticks() function returns the current number
803  * of timer ticks in 10msec intervals. This function is suitable timestamping
804  * and calculating time intervals by calculating the difference between two
805  * timestamps.
806  *
807  * Return:
808  * The current system tick count (in 10msec intervals).  This
809  * function cannot fail.
810  */
811 unsigned long qdf_mc_timer_get_system_ticks(void)
812 {
813 	return jiffies_to_msecs(jiffies) / 10;
814 }
815 qdf_export_symbol(qdf_mc_timer_get_system_ticks);
816 
817 /**
818  * qdf_mc_timer_get_system_time() - Get the system time in milliseconds
819  *
820  * qdf_mc_timer_get_system_time() function returns the number of milliseconds
821  * that have elapsed since the system was started
822  *
823  * Return:
824  * The current system time in milliseconds
825  */
826 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
827 unsigned long qdf_mc_timer_get_system_time(void)
828 {
829 	struct timespec64 tv;
830 
831 	ktime_get_real_ts64(&tv);
832 	return tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
833 }
834 qdf_export_symbol(qdf_mc_timer_get_system_time);
835 
836 #else
837 unsigned long qdf_mc_timer_get_system_time(void)
838 {
839 	struct timeval tv;
840 
841 	do_gettimeofday(&tv);
842 	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
843 }
844 qdf_export_symbol(qdf_mc_timer_get_system_time);
845 #endif
846 
847 s64 qdf_get_monotonic_boottime_ns(void)
848 {
849 	return ktime_to_ns(ktime_get_boottime());
850 }
851 qdf_export_symbol(qdf_get_monotonic_boottime_ns);
852 
853 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
854 qdf_time_t qdf_get_time_of_the_day_ms(void)
855 {
856 	struct timespec64 tv;
857 	qdf_time_t local_time;
858 	struct rtc_time tm;
859 
860 	ktime_get_real_ts64(&tv);
861 	local_time = (qdf_time_t)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));
862 	rtc_time_to_tm(local_time, &tm);
863 
864 	return (tm.tm_hour * 60 * 60 * 1000) +
865 		(tm.tm_min * 60 * 1000) + (tm.tm_sec * 1000) +
866 		(tv.tv_nsec / 1000000);
867 }
868 qdf_export_symbol(qdf_get_time_of_the_day_ms);
869 
870 #else
871 qdf_time_t qdf_get_time_of_the_day_ms(void)
872 {
873 	struct timeval tv;
874 	qdf_time_t local_time;
875 	struct rtc_time tm;
876 
877 	do_gettimeofday(&tv);
878 	local_time = (qdf_time_t)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));
879 	rtc_time_to_tm(local_time, &tm);
880 
881 	return (tm.tm_hour * 60 * 60 * 1000) +
882 		(tm.tm_min * 60 * 1000) + (tm.tm_sec * 1000) +
883 		(tv.tv_usec / 1000);
884 }
885 qdf_export_symbol(qdf_get_time_of_the_day_ms);
886 #endif
887 
888 /**
889  * qdf_timer_module_deinit() - Deinitializes a QDF timer module.
890  *
891  * This API deinitializes the QDF timer module.
892  * Return: none
893  */
894 void qdf_timer_module_deinit(void)
895 {
896 	QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
897 		  "De-Initializing the QDF MC timer module");
898 	qdf_mutex_destroy(&persistent_timer_count_lock);
899 }
900 qdf_export_symbol(qdf_timer_module_deinit);
901 
902 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
903 void qdf_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len)
904 {
905 	struct timespec64 tv;
906 	struct rtc_time tm;
907 	unsigned long local_time;
908 
909 	/* Format the Log time R#: [hr:min:sec.microsec] */
910 	ktime_get_real_ts64(&tv);
911 	/* Convert rtc to local time */
912 	local_time = (u32)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));
913 	rtc_time_to_tm(local_time, &tm);
914 	scnprintf(tbuf, len,
915 		  "[%02d:%02d:%02d.%06lu]",
916 		  tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_nsec / 1000);
917 }
918 qdf_export_symbol(qdf_get_time_of_the_day_in_hr_min_sec_usec);
919 
920 #else
921 void qdf_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len)
922 {
923 	struct timeval tv;
924 	struct rtc_time tm;
925 	unsigned long local_time;
926 
927 	/* Format the Log time R#: [hr:min:sec.microsec] */
928 	do_gettimeofday(&tv);
929 	/* Convert rtc to local time */
930 	local_time = (u32)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));
931 	rtc_time_to_tm(local_time, &tm);
932 	scnprintf(tbuf, len,
933 		"[%02d:%02d:%02d.%06lu]",
934 		tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec);
935 }
936 qdf_export_symbol(qdf_get_time_of_the_day_in_hr_min_sec_usec);
937 #endif
938