/*
 * Copyright (c) 2022 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Public SMBus Driver APIs
 */

#ifndef ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_
#define ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_

/**
 * @brief SMBus Interface
 * @defgroup smbus_interface SMBus Interface
 * @since 3.4
 * @version 0.1.0
 * @ingroup io_interfaces
 * @{
 */

#include <errno.h>
#include <zephyr/sys/slist.h>
#include <zephyr/types.h>
#include <zephyr/device.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @name SMBus Protocol commands
 * @{
 *
 * SMBus Specification defines the following SMBus protocols operations
 */

/**
 * SMBus Quick protocol is a very simple command with no data sent or
 * received. Peripheral may denote only R/W bit, which can still be
 * used for the peripheral management, for example to switch peripheral
 * On/Off. Quick protocol can also be used for peripheral devices
 * scanning.
 *
 * @code
 * 0                   1
 * 0 1 2 3 4 5 6 7 8 9 0
 * +-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |D|A|P|
 * +-+-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_QUICK			0b000

/**
 * SMBus Byte protocol can send or receive one byte of data.
 *
 * @code
 * Byte Write
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A| Command code  |A|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * Byte Read
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |R|A| Byte received |N|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_BYTE			0b001

/**
 * SMBus Byte Data protocol sends the first byte (command) followed
 * by read or write one byte.
 *
 * @code
 * Byte Data Write
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A|   Data Write  |A|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * Byte Data Read
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A|S| Periph Addr |R|A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Data Read   |N|P|
 * +-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_BYTE_DATA		0b010

/**
 * SMBus Word Data protocol sends the first byte (command) followed
 * by read or write two bytes.
 *
 * @code
 * Word Data Write
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A| Data Write Low|A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | Data Write Hi |A|P|
 * +-+-+-+-+-+-+-+-+-+-+
 *
 * Word Data Read
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A|S| Periph Addr |R|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |A| Data Read Low |A|  Data Read Hi |N|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_WORD_DATA		0b011

/**
 * SMBus Process Call protocol is Write Word followed by
 * Read Word. It is named so because the command sends data and waits
 * for the peripheral to return a reply.
 *
 * @code
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A| Data Write Low|A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | Data Write Hi |A|S| Periph Addr |R|A| Data Read Low |A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | Data Read Hi  |N|P|
 * +-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_PROC_CALL		0b100

/**
 * SMBus Block protocol reads or writes a block of data up to 32 bytes.
 * The Count byte specifies the amount of data.
 *
 * @code
 *
 * SMBus Block Write
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A| Send Count=N  |A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |  Data Write 1 |A|      ...      |A|  Data Write N |A|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * SMBus Block Read
 *
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A|S| Periph Addr |R|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |A|  Recv Count=N |A|  Data Read 1  |A|      ...      |A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |  Data Read N  |N|P|
 * +-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_BLOCK			0b101

/**
 * SMBus Block Write - Block Read Process Call protocol is
 * Block Write followed by Block Read.
 *
 * @code
 * 0                   1                   2
 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |S| Periph Addr |W|A|  Command code |A|   Count = N   |A|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |  Data Write 1 |A|      ...      |A|  Data Write N |A|S|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | Periph Addr |R|A|  Recv Count=N |A|  Data Read 1  |A| |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |     ...     |A|  Data Read N  |N|P|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * @endcode
 */
#define SMBUS_CMD_BLOCK_PROC		0b111
/** @} */

/** Maximum number of bytes in SMBus Block protocol */
#define SMBUS_BLOCK_BYTES_MAX		32

/**
 * @name SMBus device functionality
 * @{
 *
 * The following parameters describe the functionality of the SMBus device
 */

/** Peripheral to act as Controller. */
#define SMBUS_MODE_CONTROLLER		BIT(0)

/** Support Packet Error Code (PEC) checking */
#define SMBUS_MODE_PEC			BIT(1)

/** Support Host Notify functionality */
#define SMBUS_MODE_HOST_NOTIFY		BIT(2)

/** Support SMBALERT signal functionality */
#define SMBUS_MODE_SMBALERT		BIT(3)

/** @} */

/**
 * @name SMBus special reserved addresses
 * @{
 *
 * The following addresses are reserved by SMBus specification
 */

/**
 * @brief Alert Response Address (ARA)
 *
 * A broadcast address used by the system host as part of the
 * Alert Response Protocol.
 */
#define SMBUS_ADDRESS_ARA		0x0c

/** @} */

/**
 * @name SMBus read / write direction
 * @{
 */

/** @brief SMBus read / write direction */
enum smbus_direction {
	/** Write a message to SMBus peripheral */
	SMBUS_MSG_WRITE = 0,
	/** Read a message from SMBus peripheral */
	SMBUS_MSG_READ = 1,
};

/** @} */

/** @cond INTERNAL_HIDDEN */
#define SMBUS_MSG_RW_MASK		BIT(0)
/** @endcond  */

struct smbus_callback;

/**
 * @brief Define SMBus callback handler function signature.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param cb Structure smbus_callback owning this handler.
 * @param addr Address of the SMBus peripheral device.
 */
typedef void (*smbus_callback_handler_t)(const struct device *dev,
					 struct smbus_callback *cb,
					 uint8_t addr);

/**
 * @brief SMBus callback structure
 *
 * Used to register a callback in the driver instance callback list.
 * As many callbacks as needed can be added as long as each of them
 * is a unique pointer of struct smbus_callback.
 *
 * Note: Such struct should not be allocated on stack.
 */
struct smbus_callback {
	/** This should be used in driver for a callback list management */
	sys_snode_t node;

	/** Actual callback function being called when relevant */
	smbus_callback_handler_t handler;

	/** Peripheral device address */
	uint8_t addr;
};

/**
 * @brief Complete SMBus DT information
 */
struct smbus_dt_spec {
	/** SMBus bus */
	const struct device *bus;
	/** Address of the SMBus peripheral device */
	uint16_t addr;
};

/**
 * @brief Structure initializer for smbus_dt_spec from devicetree
 *
 * This helper macro expands to a static initializer for a <tt>struct
 * smbus_dt_spec</tt> by reading the relevant bus and address data from
 * the devicetree.
 *
 * @param node_id Devicetree node identifier for the SMBus device whose
 *                struct smbus_dt_spec to create an initializer for
 */
#define SMBUS_DT_SPEC_GET(node_id)				\
	{							\
		.bus = DEVICE_DT_GET(DT_BUS(node_id)),		\
		.addr = DT_REG_ADDR(node_id)			\
	}

/**
 * @brief Structure initializer for smbus_dt_spec from devicetree instance
 *
 * This is equivalent to
 * <tt>SMBUS_DT_SPEC_GET(DT_DRV_INST(inst))</tt>.
 *
 * @param inst Devicetree instance number
 */
#define SMBUS_DT_SPEC_INST_GET(inst) SMBUS_DT_SPEC_GET(DT_DRV_INST(inst))

/**
 * @cond INTERNAL_HIDDEN
 *
 * These are for internal use only, so skip these in
 * public documentation.
 */

typedef int (*smbus_api_configure_t)(const struct device *dev,
				     uint32_t dev_config);
typedef int (*smbus_api_get_config_t)(const struct device *dev,
				      uint32_t *dev_config);
typedef int (*smbus_api_quick_t)(const struct device *dev,
				 uint16_t addr, enum smbus_direction);
typedef int (*smbus_api_byte_write_t)(const struct device *dev,
				      uint16_t addr, uint8_t byte);
typedef int (*smbus_api_byte_read_t)(const struct device *dev,
				     uint16_t addr, uint8_t *byte);
typedef int (*smbus_api_byte_data_write_t)(const struct device *dev,
					   uint16_t addr, uint8_t cmd,
					   uint8_t byte);
typedef int (*smbus_api_byte_data_read_t)(const struct device *dev,
					  uint16_t addr, uint8_t cmd,
					  uint8_t *byte);
typedef int (*smbus_api_word_data_write_t)(const struct device *dev,
					   uint16_t addr, uint8_t cmd,
					   uint16_t word);
typedef int (*smbus_api_word_data_read_t)(const struct device *dev,
					  uint16_t addr, uint8_t cmd,
					  uint16_t *word);
typedef int (*smbus_api_pcall_t)(const struct device *dev,
				 uint16_t addr, uint8_t cmd,
				 uint16_t send_word, uint16_t *recv_word);
typedef int (*smbus_api_block_write_t)(const struct device *dev,
				       uint16_t addr, uint8_t cmd,
				       uint8_t count, uint8_t *buf);
typedef int (*smbus_api_block_read_t)(const struct device *dev,
				      uint16_t addr, uint8_t cmd,
				      uint8_t *count, uint8_t *buf);
typedef int (*smbus_api_block_pcall_t)(const struct device *dev,
				       uint16_t addr, uint8_t cmd,
				       uint8_t send_count, uint8_t *send_buf,
				       uint8_t *recv_count, uint8_t *recv_buf);
typedef int (*smbus_api_smbalert_cb_t)(const struct device *dev,
				       struct smbus_callback *cb);
typedef int (*smbus_api_host_notify_cb_t)(const struct device *dev,
					  struct smbus_callback *cb);

__subsystem struct smbus_driver_api {
	smbus_api_configure_t configure;
	smbus_api_get_config_t get_config;
	smbus_api_quick_t smbus_quick;
	smbus_api_byte_write_t smbus_byte_write;
	smbus_api_byte_read_t smbus_byte_read;
	smbus_api_byte_data_write_t smbus_byte_data_write;
	smbus_api_byte_data_read_t smbus_byte_data_read;
	smbus_api_word_data_write_t smbus_word_data_write;
	smbus_api_word_data_read_t smbus_word_data_read;
	smbus_api_pcall_t smbus_pcall;
	smbus_api_block_write_t smbus_block_write;
	smbus_api_block_read_t smbus_block_read;
	smbus_api_block_pcall_t smbus_block_pcall;
	smbus_api_smbalert_cb_t smbus_smbalert_set_cb;
	smbus_api_smbalert_cb_t smbus_smbalert_remove_cb;
	smbus_api_host_notify_cb_t smbus_host_notify_set_cb;
	smbus_api_host_notify_cb_t smbus_host_notify_remove_cb;
};

/**
 * @endcond
 */

#if defined(CONFIG_SMBUS_STATS) || defined(__DOXYGEN__)

#include <zephyr/stats/stats.h>

/** @cond INTERNAL_HIDDEN */

STATS_SECT_START(smbus)
STATS_SECT_ENTRY32(bytes_read)
STATS_SECT_ENTRY32(bytes_written)
STATS_SECT_ENTRY32(command_count)
STATS_SECT_END;

STATS_NAME_START(smbus)
STATS_NAME(smbus, bytes_read)
STATS_NAME(smbus, bytes_written)
STATS_NAME(smbus, command_count)
STATS_NAME_END(smbus);

struct smbus_device_state {
	struct device_state devstate;
	struct stats_smbus stats;
};

/**
 * @brief Define a statically allocated and section assigned smbus device state
 */
#define Z_SMBUS_DEVICE_STATE_DEFINE(node_id, dev_name)			\
	static struct smbus_device_state Z_DEVICE_STATE_NAME(dev_name)	\
	__attribute__((__section__(".z_devstate")));

/**
 * @brief Define an smbus device init wrapper function
 *
 * This does device instance specific initialization of common data
 * (such as stats) and calls the given init_fn
 */
#define Z_SMBUS_INIT_FN(dev_name, init_fn)				\
	static inline int						\
		UTIL_CAT(dev_name, _init)(const struct device *dev)	\
	{								\
		struct smbus_device_state *state =			\
			CONTAINER_OF(dev->state,			\
				     struct smbus_device_state,		\
				     devstate);				\
		stats_init(&state->stats.s_hdr, STATS_SIZE_32, 4,	\
			   STATS_NAME_INIT_PARMS(smbus));		\
		stats_register(dev->name, &(state->stats.s_hdr));	\
		return init_fn(dev);					\
	}

/** @endcond */

/**
 * @brief Updates the SMBus stats
 *
 * @param dev Pointer to the device structure for the SMBus driver instance
 * to update stats for.
 * @param sent Number of bytes sent
 * @param recv Number of bytes received
 */
static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent,
				    uint8_t recv)
{
	struct smbus_device_state *state =
		CONTAINER_OF(dev->state, struct smbus_device_state, devstate);

	STATS_INC(state->stats, command_count);
	STATS_INCN(state->stats, bytes_read, recv);
	STATS_INCN(state->stats, bytes_written, sent);
}

/**
 * @brief Like DEVICE_DT_DEFINE() with SMBus specifics.
 *
 * @details Defines a device which implements the SMBus API. May
 * generate a custom device_state container struct and init_fn
 * wrapper when needed depending on SMBus @kconfig{CONFIG_SMBUS_STATS}.
 *
 * @param node_id The devicetree node identifier.
 *
 * @param init_fn Name of the init function of the driver.
 *
 * @param pm_device PM device resources reference
 * (NULL if device does not use PM).
 *
 * @param data_ptr Pointer to the device's private data.
 *
 * @param cfg_ptr The address to the structure containing the
 * configuration information for this instance of the driver.
 *
 * @param level The initialization level. See SYS_INIT() for
 * details.
 *
 * @param prio Priority within the selected initialization level. See
 * SYS_INIT() for details.
 *
 * @param api_ptr Provides an initial pointer to the API function struct
 * used by the driver. Can be NULL.
 */
#define SMBUS_DEVICE_DT_DEFINE(node_id, init_fn, pm_device,		\
			       data_ptr, cfg_ptr, level, prio,		\
			       api_ptr, ...)				\
	Z_SMBUS_DEVICE_STATE_DEFINE(node_id,				\
				    Z_DEVICE_DT_DEV_NAME(node_id));	\
	Z_SMBUS_INIT_FN(Z_DEVICE_DT_DEV_NAME(node_id), init_fn)		\
	Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id),		\
			DEVICE_DT_NAME(node_id),			\
			&UTIL_CAT(Z_DEVICE_DT_DEV_NAME(node_id), _init),\
			pm_device,					\
			data_ptr, cfg_ptr, level, prio,			\
			api_ptr,					\
			&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME	\
					      (node_id)).devstate),	\
			__VA_ARGS__)

#else /* CONFIG_SMBUS_STATS */

static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent,
				    uint8_t recv)
{
	ARG_UNUSED(dev);
	ARG_UNUSED(sent);
	ARG_UNUSED(recv);
}

#define SMBUS_DEVICE_DT_DEFINE(node_id, init_fn, pm_device,		\
			     data_ptr, cfg_ptr, level, prio,		\
			     api_ptr, ...)				\
	DEVICE_DT_DEFINE(node_id, &init_fn, pm_device,			\
			     data_ptr, cfg_ptr, level, prio,		\
			     api_ptr, __VA_ARGS__)

#endif /* CONFIG_SMBUS_STATS */

/**
 * @brief Like SMBUS_DEVICE_DT_DEFINE() for an instance of a DT_DRV_COMPAT
 * compatible
 *
 * @param inst instance number. This is replaced by
 * <tt>DT_DRV_COMPAT(inst)</tt> in the call to SMBUS_DEVICE_DT_DEFINE().
 *
 * @param ... other parameters as expected by SMBUS_DEVICE_DT_DEFINE().
 */
#define SMBUS_DEVICE_DT_INST_DEFINE(inst, ...)		\
	SMBUS_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)

/**
 * @brief Configure operation of a SMBus host controller.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param dev_config Bit-packed 32-bit value to the device runtime configuration
 * for the SMBus controller.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 */
__syscall int smbus_configure(const struct device *dev, uint32_t dev_config);

static inline int z_impl_smbus_configure(const struct device *dev,
					 uint32_t dev_config)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	return api->configure(dev, dev_config);
}

/**
 * @brief Get configuration of a SMBus host controller.
 *
 * This routine provides a way to get current configuration. It is allowed to
 * call the function before smbus_configure, because some SMBus ports can be
 * configured during init process. However, if the SMBus port is not configured,
 * smbus_get_config returns an error.
 *
 * smbus_get_config can return cached config or probe hardware, but it has to be
 * up to date with current configuration.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param dev_config Pointer to return bit-packed 32-bit value of
 * the SMBus controller configuration.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_get_config() is not implemented
 * by the driver.
 */
__syscall int smbus_get_config(const struct device *dev, uint32_t *dev_config);

static inline int z_impl_smbus_get_config(const struct device *dev,
					  uint32_t *dev_config)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->get_config == NULL) {
		return -ENOSYS;
	}

	return api->get_config(dev, dev_config);
}

/**
 * @brief Add SMBUSALERT callback for a SMBus host controller.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param cb Pointer to a callback structure.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_smbalert_set_cb() is not implemented
 * by the driver.
 */
static inline int smbus_smbalert_set_cb(const struct device *dev,
					struct smbus_callback *cb)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_smbalert_set_cb == NULL) {
		return -ENOSYS;
	}

	return api->smbus_smbalert_set_cb(dev, cb);
}

/**
 * @brief Remove SMBUSALERT callback from a SMBus host controller.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param cb Pointer to a callback structure.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_smbalert_remove_cb() is not implemented
 * by the driver.
 */
__syscall int smbus_smbalert_remove_cb(const struct device *dev,
				       struct smbus_callback *cb);

static inline int z_impl_smbus_smbalert_remove_cb(const struct device *dev,
						  struct smbus_callback *cb)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_smbalert_remove_cb == NULL) {
		return -ENOSYS;
	}

	return api->smbus_smbalert_remove_cb(dev, cb);
}

/**
 * @brief Add Host Notify callback for a SMBus host controller.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param cb Pointer to a callback structure.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_host_notify_set_cb() is not implemented
 * by the driver.
 */
static inline int smbus_host_notify_set_cb(const struct device *dev,
					   struct smbus_callback *cb)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_host_notify_set_cb == NULL) {
		return -ENOSYS;
	}

	return api->smbus_host_notify_set_cb(dev, cb);
}

/**
 * @brief Remove Host Notify callback from a SMBus host controller.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param cb Pointer to a callback structure.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_host_notify_remove_cb() is not implemented
 * by the driver.
 */
__syscall int smbus_host_notify_remove_cb(const struct device *dev,
					  struct smbus_callback *cb);

static inline int z_impl_smbus_host_notify_remove_cb(const struct device *dev,
						     struct smbus_callback *cb)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_host_notify_remove_cb == NULL) {
		return -ENOSYS;
	}

	return api->smbus_host_notify_remove_cb(dev, cb);
}

/**
 * @brief Perform SMBus Quick operation
 *
 * This routine provides a generic interface to perform SMBus Quick
 * operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * driver configured in controller mode.
 * @param addr Address of the SMBus peripheral device.
 * @param direction Direction Read or Write.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_quick() is not implemented
 * by the driver.
 */
__syscall int smbus_quick(const struct device *dev, uint16_t addr,
			  enum smbus_direction direction);

static inline int z_impl_smbus_quick(const struct device *dev, uint16_t addr,
				     enum smbus_direction direction)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_quick == NULL) {
		return -ENOSYS;
	}

	if (direction != SMBUS_MSG_READ && direction != SMBUS_MSG_WRITE) {
		return -EINVAL;
	}

	return  api->smbus_quick(dev, addr, direction);
}

/**
 * @brief Perform SMBus Byte Write operation
 *
 * This routine provides a generic interface to perform SMBus
 * Byte Write operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param byte Byte to be sent to the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_byte_write() is not implemented
 * by the driver.
 */
__syscall int smbus_byte_write(const struct device *dev, uint16_t addr,
			       uint8_t byte);

static inline int z_impl_smbus_byte_write(const struct device *dev,
					  uint16_t addr, uint8_t byte)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_byte_write == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_byte_write(dev, addr, byte);
}

/**
 * @brief Perform SMBus Byte Read operation
 *
 * This routine provides a generic interface to perform SMBus
 * Byte Read operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param byte Byte received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_byte_read() is not implemented
 * by the driver.
 */
__syscall int smbus_byte_read(const struct device *dev, uint16_t addr,
			      uint8_t *byte);

static inline int z_impl_smbus_byte_read(const struct device *dev,
					 uint16_t addr, uint8_t *byte)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_byte_read == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_byte_read(dev, addr, byte);
}

/**
 * @brief Perform SMBus Byte Data Write operation
 *
 * This routine provides a generic interface to perform SMBus
 * Byte Data Write operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param byte Byte to be sent to the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_byte_data_write() is not implemented
 * by the driver.
 */
__syscall int smbus_byte_data_write(const struct device *dev, uint16_t addr,
				    uint8_t cmd, uint8_t byte);

static inline int z_impl_smbus_byte_data_write(const struct device *dev,
					       uint16_t addr, uint8_t cmd,
					       uint8_t byte)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_byte_data_write == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_byte_data_write(dev, addr, cmd, byte);
}

/**
 * @brief Perform SMBus Byte Data Read operation
 *
 * This routine provides a generic interface to perform SMBus
 * Byte Data Read operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param byte Byte received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_byte_data_read() is not implemented
 * by the driver.
 */
__syscall int smbus_byte_data_read(const struct device *dev, uint16_t addr,
				   uint8_t cmd, uint8_t *byte);

static inline int z_impl_smbus_byte_data_read(const struct device *dev,
					      uint16_t addr, uint8_t cmd,
					      uint8_t *byte)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_byte_data_read == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_byte_data_read(dev, addr, cmd, byte);
}

/**
 * @brief Perform SMBus Word Data Write operation
 *
 * This routine provides a generic interface to perform SMBus
 * Word Data Write operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param word Word (16-bit) to be sent to the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_word_data_write() is not implemented
 * by the driver.
 */
__syscall int smbus_word_data_write(const struct device *dev, uint16_t addr,
				    uint8_t cmd, uint16_t word);

static inline int z_impl_smbus_word_data_write(const struct device *dev,
					       uint16_t addr, uint8_t cmd,
					       uint16_t word)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_word_data_write == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_word_data_write(dev, addr, cmd, word);
}

/**
 * @brief Perform SMBus Word Data Read operation
 *
 * This routine provides a generic interface to perform SMBus
 * Word Data Read operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param word Word (16-bit) received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_word_data_read() is not implemented
 * by the driver.
 */
__syscall int smbus_word_data_read(const struct device *dev, uint16_t addr,
				   uint8_t cmd, uint16_t *word);

static inline int z_impl_smbus_word_data_read(const struct device *dev,
					      uint16_t addr, uint8_t cmd,
					      uint16_t *word)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_word_data_read == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_word_data_read(dev, addr, cmd, word);
}

/**
 * @brief Perform SMBus Process Call operation
 *
 * This routine provides a generic interface to perform SMBus
 * Process Call operation, which means Write 2 bytes following by
 * Read 2 bytes.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param send_word Word (16-bit) to be sent to the peripheral device.
 * @param recv_word Word (16-bit) received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_pcall() is not implemented
 * by the driver.
 */
__syscall int smbus_pcall(const struct device *dev, uint16_t addr,
			  uint8_t cmd, uint16_t send_word, uint16_t *recv_word);

static inline int z_impl_smbus_pcall(const struct device *dev,
				     uint16_t addr, uint8_t cmd,
				     uint16_t send_word, uint16_t *recv_word)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_pcall == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_pcall(dev, addr, cmd, send_word, recv_word);
}

/**
 * @brief Perform SMBus Block Write operation
 *
 * This routine provides a generic interface to perform SMBus
 * Block Write operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param count Size of the data block buffer. Maximum 32 bytes.
 * @param buf Data block buffer to be sent to the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_block_write() is not implemented
 * by the driver.
 */
__syscall int smbus_block_write(const struct device *dev, uint16_t addr,
				uint8_t cmd, uint8_t count, uint8_t *buf);

static inline int z_impl_smbus_block_write(const struct device *dev,
					   uint16_t addr, uint8_t cmd,
					   uint8_t count, uint8_t *buf)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_block_write == NULL) {
		return -ENOSYS;
	}

	if (count < 1 || count > SMBUS_BLOCK_BYTES_MAX) {
		return -EINVAL;
	}

	return  api->smbus_block_write(dev, addr, cmd, count, buf);
}

/**
 * @brief Perform SMBus Block Read operation
 *
 * This routine provides a generic interface to perform SMBus
 * Block Read operation.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param count Size of the data peripheral sent. Maximum 32 bytes.
 * @param buf Data block buffer received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_block_read() is not implemented
 * by the driver.
 */
__syscall int smbus_block_read(const struct device *dev, uint16_t addr,
			       uint8_t cmd, uint8_t *count, uint8_t *buf);

static inline int z_impl_smbus_block_read(const struct device *dev,
					  uint16_t addr, uint8_t cmd,
					  uint8_t *count, uint8_t *buf)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_block_read == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_block_read(dev, addr, cmd, count, buf);
}

/**
 * @brief Perform SMBus Block Process Call operation
 *
 * This routine provides a generic interface to perform SMBus
 * Block Process Call operation. This operation is basically
 * Block Write followed by Block Read.
 *
 * @param dev Pointer to the device structure for the SMBus driver instance.
 * @param addr Address of the SMBus peripheral device.
 * @param cmd Command byte which is sent to peripheral device first.
 * @param snd_count Size of the data block buffer to send.
 * @param snd_buf Data block buffer send to the peripheral device.
 * @param rcv_count Size of the data peripheral sent.
 * @param rcv_buf Data block buffer received from the peripheral device.
 *
 * @retval 0 If successful.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS If function smbus_block_pcall() is not implemented
 * by the driver.
 */
__syscall int smbus_block_pcall(const struct device *dev,
				uint16_t addr, uint8_t cmd,
				uint8_t snd_count, uint8_t *snd_buf,
				uint8_t *rcv_count, uint8_t *rcv_buf);

static inline int z_impl_smbus_block_pcall(const struct device *dev,
					   uint16_t addr, uint8_t cmd,
					   uint8_t snd_count, uint8_t *snd_buf,
					   uint8_t *rcv_count, uint8_t *rcv_buf)
{
	const struct smbus_driver_api *api =
		(const struct smbus_driver_api *)dev->api;

	if (api->smbus_block_pcall == NULL) {
		return -ENOSYS;
	}

	return  api->smbus_block_pcall(dev, addr, cmd, snd_count, snd_buf,
				       rcv_count, rcv_buf);
}

#ifdef __cplusplus
}
#endif

/**
 * @}
 */

#include <zephyr/syscalls/smbus.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_ */
