/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright 2019 Broadcom.
 */

#ifndef __DRIVERS_WDT_H
#define __DRIVERS_WDT_H

#include <assert.h>
#include <kernel/interrupt.h>
#include <kernel/thread.h>
#include <sm/sm.h>
#include <tee_api_types.h>

struct wdt_chip {
	const struct wdt_ops *ops;
	struct itr_handler *wdt_itr;
};

/*
 * struct wdt_ops - The watchdog device operations
 *
 * @init:	The routine to initialized the watchdog resources.
 * @start:	The routine for starting the watchdog device.
 * @stop:	The routine for stopping the watchdog device.
 * @ping:	The routine that sends a keepalive ping to the watchdog device.
 * @set_timeout:The routine that finds the load value that will reset system in
 * required timeout (in seconds).
 * @get_timeleft:The optional routine that finds if the watchdog is already
 * started and the time left (in seconds) before the watchdog timeouts.
 *
 * The wdt_ops structure contains a list of low-level operations
 * that control a watchdog device.
 */
struct wdt_ops {
	TEE_Result (*init)(struct wdt_chip *chip, unsigned long *min_timeout,
			   unsigned long *max_timeout);
	void (*start)(struct wdt_chip *chip);
	void (*stop)(struct wdt_chip *chip);
	void (*ping)(struct wdt_chip *chip);
	TEE_Result (*set_timeout)(struct wdt_chip *chip, unsigned long timeout);
	TEE_Result (*get_timeleft)(struct wdt_chip *chip, bool *is_started,
				   unsigned long *timeleft);
};

#ifdef CFG_WDT
extern struct wdt_chip *wdt_chip;

/* Register a watchdog as the system watchdog */
TEE_Result watchdog_register(struct wdt_chip *chip);

static inline
TEE_Result watchdog_init(unsigned long *min_timeout, unsigned long *max_timeout)
{
	if (!wdt_chip)
		return TEE_ERROR_NOT_SUPPORTED;

	if (!wdt_chip->ops->init)
		return TEE_SUCCESS;

	return wdt_chip->ops->init(wdt_chip, min_timeout, max_timeout);
}

static inline void watchdog_start(void)
{
	if (wdt_chip)
		wdt_chip->ops->start(wdt_chip);
}

static inline void watchdog_stop(void)
{
	if (wdt_chip && wdt_chip->ops->stop)
		wdt_chip->ops->stop(wdt_chip);
}

static inline void watchdog_ping(void)
{
	if (wdt_chip)
		wdt_chip->ops->ping(wdt_chip);
}

static inline void watchdog_settimeout(unsigned long timeout)
{
	if (wdt_chip)
		wdt_chip->ops->set_timeout(wdt_chip, timeout);
}

/*
 * watchdog_gettimeleft() - Get the time left before the watchdog timeouts
 * @is_started: [out] true if watchdog has started, false otherwise
 * @timeleft: [out] time left in seconds before expiration if @is_started==true
 */
static inline TEE_Result watchdog_gettimeleft(bool *is_started,
					      unsigned long *timeleft)
{
	if (!wdt_chip || !wdt_chip->ops->get_timeleft)
		return TEE_ERROR_NOT_SUPPORTED;

	return wdt_chip->ops->get_timeleft(wdt_chip, is_started, timeleft);
}
#else
static inline TEE_Result watchdog_register(struct wdt_chip *chip __unused)
{
	return TEE_ERROR_NOT_SUPPORTED;
}

static inline TEE_Result watchdog_init(unsigned long *min_timeout __unused,
				       unsigned long *max_timeout __unused)
{
	return TEE_ERROR_NOT_SUPPORTED;
}

static inline void watchdog_start(void) {}
static inline void watchdog_stop(void) {}
static inline void watchdog_ping(void) {}
static inline void watchdog_settimeout(unsigned long timeout __unused) {}
static inline TEE_Result watchdog_gettimeleft(bool *is_started __unused,
					      unsigned long *timeleft __unused)
{
	return TEE_ERROR_NOT_SUPPORTED;
}
#endif

#ifdef CFG_WDT_SM_HANDLER
enum sm_handler_ret __wdt_sm_handler(struct thread_smc_args *args);

static inline
enum sm_handler_ret wdt_sm_handler(struct thread_smc_args *args)
{
	if (args->a0 != CFG_WDT_SM_HANDLER_ID)
		return SM_HANDLER_PENDING_SMC;

	return __wdt_sm_handler(args);
}
#else
static inline
enum sm_handler_ret wdt_sm_handler(struct thread_smc_args *args __unused)
{
	return SM_HANDLER_PENDING_SMC;
}
#endif

#endif /* __DRIVERS_WDT_H */
