/*
 * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/**
 * DOC: i_qdf_hrtimer
 * This file provides OS dependent timer API's.
 */

#ifndef _I_QDF_HRTIMER_H
#define _I_QDF_HRTIMER_H

#include <linux/version.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <qdf_types.h>

/* hrtimer data type */
typedef struct {
	union {
		struct hrtimer hrtimer;
		struct tasklet_hrtimer tasklet_hrtimer;
	} u;
	enum qdf_context_mode ctx;
} __qdf_hrtimer_data_t;

/**
 * __qdf_hrtimer_start() - Starts hrtimer in given context
 * @timer: pointer to the hrtimer object
 * @interval: interval to forward as qdf_ktime_t object
 * @mode: mode of hrtimer
 *
 * Starts hrtimer in given context
 *
 * Return: void
 */
static inline
void __qdf_hrtimer_start(__qdf_hrtimer_data_t *timer, ktime_t interval,
			 enum qdf_hrtimer_mode mode)
{
	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		hrtimer_start(&timer->u.hrtimer, interval, mode);
	else if (timer->ctx == QDF_CONTEXT_TASKLET)
		tasklet_hrtimer_start(&timer->u.tasklet_hrtimer,
				      interval, mode);
}

/**
 * __qdf_hrtimer_cancel() - cancels hrtimer in given context
 * @timer: pointer to the hrtimer object
 *
 * cancels hrtimer in given context
 *
 * Return: void
 */
static inline
void __qdf_hrtimer_cancel(__qdf_hrtimer_data_t *timer)
{
	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		hrtimer_cancel(&timer->u.hrtimer);
	else if (timer->ctx == QDF_CONTEXT_TASKLET)
		hrtimer_cancel(&timer->u.tasklet_hrtimer.timer);
}

/**
 * __qdf_hrtimer_init() - init hrtimer in a given context
 * @timer: pointer to the hrtimer object
 * @cback: callback function to be fired
 * @clock: clock id
 * @hrtimer_mode: mode of hrtimer
 *
 * starts hrtimer in a context passed as per the context
 *
 * Return: void
 */
static inline void  __qdf_hrtimer_init(__qdf_hrtimer_data_t *timer,
				       void *cback,
				       enum qdf_clock_id clock,
				       enum qdf_hrtimer_mode mode,
				       enum qdf_context_mode ctx)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	timer->ctx = ctx;

	if (timer->ctx == QDF_CONTEXT_HARDWARE) {
		hrtimer_init(hrtimer, clock, mode);
		hrtimer->function = cback;
	} else if (timer->ctx == QDF_CONTEXT_TASKLET) {
		tasklet_hrtimer_init(tasklet_hrtimer, cback, clock, mode);
	}
}

/**
 * __qdf_hrtimer_kill() - kills hrtimer in given context
 * @timer: pointer to the hrtimer object
 *
 * kills hrtimer in given context
 *
 * Return: void
 */
static inline
void __qdf_hrtimer_kill(__qdf_hrtimer_data_t *timer)
{
	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		hrtimer_cancel(&timer->u.hrtimer);
	else if (timer->ctx == QDF_CONTEXT_TASKLET)
		tasklet_hrtimer_cancel(&timer->u.tasklet_hrtimer);
}

/**
 * __qdf_hrtimer_get_remaining() - check remaining time in the timer
 * @timer: pointer to the hrtimer object
 *
 * check whether the timer is on one of the queues
 *
 * Return: remaining time as ktime object
 */
static inline ktime_t __qdf_hrtimer_get_remaining(__qdf_hrtimer_data_t *timer)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_get_remaining(hrtimer);
	else
		return hrtimer_get_remaining(&tasklet_hrtimer->timer);
}

/**
 * __qdf_hrtimer_is_queued() - check whether the timer is on one of the queues
 * @timer: pointer to the hrtimer object
 *
 * check whether the timer is on one of the queues
 *
 * Return: false when the timer was not in queue
 *         true when the timer was in queue
 */
static inline bool __qdf_hrtimer_is_queued(__qdf_hrtimer_data_t *timer)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_is_queued(hrtimer);
	else
		return hrtimer_is_queued(&tasklet_hrtimer->timer);
}

/**
 * __qdf_hrtimer_callback_running() - check if callback is running
 * @timer: pointer to the hrtimer object
 *
 * check whether the timer is running the callback function
 *
 * Return: false when callback is not running
 *         true when callback is running
 */
static inline bool __qdf_hrtimer_callback_running(__qdf_hrtimer_data_t *timer)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_callback_running(hrtimer);
	else
		return hrtimer_callback_running(&tasklet_hrtimer->timer);
}

/**
 * __qdf_hrtimer_active() - check if timer is active
 * @timer: pointer to the hrtimer object
 *
 * Check if timer is active. A timer is active, when it is enqueued into
 * the rbtree or the callback function is running.
 *
 * Return: false if timer is not active
 *         true if timer is active
 */
static inline bool __qdf_hrtimer_active(__qdf_hrtimer_data_t *timer)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_active(hrtimer);
	else
		return hrtimer_active(&tasklet_hrtimer->timer);
}

/**
 * __qdf_hrtimer_cb_get_time() - get remaining time in callback
 * @timer: pointer to the hrtimer object
 *
 * Get remaining time in the hrtimer callback
 *
 * Return: time remaining as ktime object
 */
static inline ktime_t __qdf_hrtimer_cb_get_time(__qdf_hrtimer_data_t *timer)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_cb_get_time(hrtimer);
	else
		return hrtimer_cb_get_time(&tasklet_hrtimer->timer);
}

/**
 * __qdf_hrtimer_forward() - forward the hrtimer
 * @timer: pointer to the hrtimer object
 * @now: current ktime
 * @interval: interval to forward as ktime object
 *
 * Forward the timer expiry so it will expire in the future
 *
 * Return:the number of overruns
 */
static inline uint64_t __qdf_hrtimer_forward(__qdf_hrtimer_data_t *timer,
					     ktime_t now,
					     ktime_t interval)
{
	struct hrtimer *hrtimer = &timer->u.hrtimer;
	struct tasklet_hrtimer *tasklet_hrtimer = &timer->u.tasklet_hrtimer;

	if (timer->ctx == QDF_CONTEXT_HARDWARE)
		return hrtimer_forward(hrtimer, now, interval);
	else
		return hrtimer_forward(&tasklet_hrtimer->timer, now, interval);
}

#endif /* _I_QDF_HRTIMER_H */
