/*
 * Copyright (c) 2017 Nordic Semiconductor ASA
 * Copyright (c) 2015 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_INCLUDE_DRIVERS_WATCHDOG_H_
#define ZEPHYR_INCLUDE_DRIVERS_WATCHDOG_H_

/**
 * @brief Watchdog Interface
 * @defgroup watchdog_interface Watchdog Interface
 * @since 1.0
 * @version 1.0.0
 * @ingroup io_interfaces
 * @{
 */

#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @name Watchdog options
 * @anchor WDT_OPT
 * @{
 */

/** @brief Pause watchdog timer when CPU is in sleep state. */
#define WDT_OPT_PAUSE_IN_SLEEP		BIT(0)

/** @brief Pause watchdog timer when CPU is halted by the debugger. */
#define WDT_OPT_PAUSE_HALTED_BY_DBG	BIT(1)

/** @} */

/**
 * @name Watchdog behavior flags
 * @anchor WDT_FLAGS
 * @{
 */

/** @cond INTERNAL_HIDDEN */
/** @brief Watchdog reset flag bit field mask shift. */
#define WDT_FLAG_RESET_SHIFT		(0)
/** @brief Watchdog reset flag bit field mask. */
#define WDT_FLAG_RESET_MASK		(0x3 << WDT_FLAG_RESET_SHIFT)
/** @endcond */

/** Reset: none */
#define WDT_FLAG_RESET_NONE		(0 << WDT_FLAG_RESET_SHIFT)
/** Reset: CPU core */
#define WDT_FLAG_RESET_CPU_CORE		(1 << WDT_FLAG_RESET_SHIFT)
/** Reset: SoC */
#define WDT_FLAG_RESET_SOC		(2 << WDT_FLAG_RESET_SHIFT)

/** @} */

/**
 * @brief Watchdog timeout window.
 *
 * Each installed timeout needs feeding within the specified time window,
 * otherwise the watchdog will trigger. If the watchdog instance does not
 * support window timeouts then min value must be equal to 0.
 *
 * @note If specified values can not be precisely set they are always rounded
 * up.
 */
struct wdt_window {
	/** Lower limit of watchdog feed timeout in milliseconds. */
	uint32_t min;
	/** Upper limit of watchdog feed timeout in milliseconds. */
	uint32_t max;
};

/**
 * @brief Watchdog callback.
 *
 * @param dev Watchdog device instance.
 * @param channel_id Channel identifier.
 */
typedef void (*wdt_callback_t)(const struct device *dev, int channel_id);

/** @brief Watchdog timeout configuration. */
struct wdt_timeout_cfg {
	/** Timing parameters of watchdog timeout. */
	struct wdt_window window;
	/** Timeout callback (can be `NULL`). */
	wdt_callback_t callback;
#if defined(CONFIG_WDT_MULTISTAGE) || defined(__DOXYGEN__)
	/**
	 * Pointer to the next timeout configuration.
	 *
	 * This field is only available if @kconfig{CONFIG_WDT_MULTISTAGE} is
	 * enabled (watchdogs with staged timeouts functionality). Value must be
	 * `NULL` for single stage timeout.
	 */
	struct wdt_timeout_cfg *next;
#endif
	/** Flags (see @ref WDT_FLAGS). */
	uint8_t flags;
};

/** @cond INTERNAL_HIDDEN */

/**
 * @brief Callback API for setting up watchdog instance.
 * @see wdt_setup().
 */
typedef int (*wdt_api_setup)(const struct device *dev, uint8_t options);

/**
 * @brief Callback API for disabling watchdog instance.
 * @see wdt_disable().
 */
typedef int (*wdt_api_disable)(const struct device *dev);

/**
 * @brief Callback API for installing new timeout.
 * @see wdt_install_timeout().
 */
typedef int (*wdt_api_install_timeout)(const struct device *dev,
				       const struct wdt_timeout_cfg *cfg);

/**
 * @brief Callback API for feeding specified watchdog timeout.
 * @see wdt_feed().
 */
typedef int (*wdt_api_feed)(const struct device *dev, int channel_id);

__subsystem struct wdt_driver_api {
	wdt_api_setup setup;
	wdt_api_disable disable;
	wdt_api_install_timeout install_timeout;
	wdt_api_feed feed;
};
/**
 * @endcond
 */

/**
 * @brief Set up watchdog instance.
 *
 * This function is used for configuring global watchdog settings that
 * affect all timeouts. It should be called after installing timeouts.
 * After successful return, all installed timeouts are valid and must be
 * serviced periodically by calling wdt_feed().
 *
 * @param dev Watchdog device instance.
 * @param options Configuration options (see @ref WDT_OPT).
 *
 * @retval 0 If successful.
 * @retval -ENOTSUP If any of the set options is not supported.
 * @retval -EBUSY If watchdog instance has been already setup.
 * @retval -errno In case of any other failure.
 */
__syscall int wdt_setup(const struct device *dev, uint8_t options);

static inline int z_impl_wdt_setup(const struct device *dev, uint8_t options)
{
	const struct wdt_driver_api *api =
		(const struct wdt_driver_api *)dev->api;

	return api->setup(dev, options);
}

/**
 * @brief Disable watchdog instance.
 *
 * This function disables the watchdog instance and automatically uninstalls all
 * timeouts. To set up a new watchdog, install timeouts and call wdt_setup()
 * again. Not all watchdogs can be restarted after they are disabled.
 *
 * @param dev Watchdog device instance.
 *
 * @retval 0 If successful.
 * @retval -EFAULT If watchdog instance is not enabled.
 * @retval -EPERM If watchdog can not be disabled directly by application code.
 * @retval -errno In case of any other failure.
 */
__syscall int wdt_disable(const struct device *dev);

static inline int z_impl_wdt_disable(const struct device *dev)
{
	const struct wdt_driver_api *api =
		(const struct wdt_driver_api *)dev->api;

	return api->disable(dev);
}

/**
 * @brief Install a new timeout.
 *
 * @note This function must be used before wdt_setup(). Changes applied here
 * have no effects until wdt_setup() is called.
 *
 * @param dev Watchdog device instance.
 * @param[in] cfg Timeout configuration.
 *
 * @retval channel_id If successful, a non-negative value indicating the index
 * of the channel to which the timeout was assigned. This value is supposed to
 * be used as the parameter in calls to wdt_feed().
 * @retval -EBUSY If timeout can not be installed while watchdog has already
 * been setup.
 * @retval -ENOMEM If no more timeouts can be installed.
 * @retval -ENOTSUP If any of the set flags is not supported.
 * @retval -EINVAL If any of the window timeout value is out of possible range.
 * This value is also returned if watchdog supports only one timeout value for
 * all timeouts and the supplied timeout window differs from windows for alarms
 * installed so far.
 * @retval -errno In case of any other failure.
 */
static inline int wdt_install_timeout(const struct device *dev,
				      const struct wdt_timeout_cfg *cfg)
{
	const struct wdt_driver_api *api =
		(const struct wdt_driver_api *) dev->api;

	return api->install_timeout(dev, cfg);
}

/**
 * @brief Feed specified watchdog timeout.
 *
 * @param dev Watchdog device instance.
 * @param channel_id Channel index.
 *
 * @retval 0 If successful.
 * @retval -EAGAIN If completing the feed operation would stall the caller, for
 * example due to an in-progress watchdog operation such as a previous
 * wdt_feed() call.
 * @retval -EINVAL If there is no installed timeout for supplied channel.
 * @retval -errno In case of any other failure.
 */
__syscall int wdt_feed(const struct device *dev, int channel_id);

static inline int z_impl_wdt_feed(const struct device *dev, int channel_id)
{
	const struct wdt_driver_api *api =
		(const struct wdt_driver_api *)dev->api;

	return api->feed(dev, channel_id);
}

#ifdef __cplusplus
}
#endif

/** @} */

#include <zephyr/syscalls/watchdog.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_WATCHDOG_H_ */
