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

#ifndef ZEPHYR_INCLUDE_DRIVERS_I3C_H_
#define ZEPHYR_INCLUDE_DRIVERS_I3C_H_

/**
 * @brief I3C Interface
 * @defgroup i3c_interface I3C Interface
 * @ingroup io_interfaces
 * @{
 */

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

#include <zephyr/drivers/i3c/addresses.h>
#include <zephyr/drivers/i3c/ccc.h>
#include <zephyr/drivers/i3c/devicetree.h>
#include <zephyr/drivers/i3c/ibi.h>
#include <zephyr/drivers/i2c.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Bus Characteristic Register (BCR)
 * - BCR[7:6]: Device Role
 *   - 0: I3C Target
 *   - 1: I3C Controller capable
 *   - 2: Reserved
 *   - 3: Reserved
 * - BCR[5]: Advanced Capabilities
 *   - 0: Does not support optional advanced capabilities.
 *   - 1: Supports optional advanced capabilities which
 *        can be viewed via GETCAPS CCC.
 * - BCR[4}: Virtual Target Support
 *   - 0: Is not a virtual target.
 *   - 1: Is a virtual target.
 * - BCR[3]: Offline Capable
 *   - 0: Will always response to I3C commands.
 *   - 1: Will not always response to I3C commands.
 * - BCR[2]: IBI Payload
 *   - 0: No data bytes following the accepted IBI.
 *   - 1: One data byte (MDB, Mandatory Data Byte) follows
 *        the accepted IBI. Additional data bytes may also
 *        follows.
 * - BCR[1]: IBI Request Capable
 *   - 0: Not capable
 *   - 1: Capable
 * - BCR[0]: Max Data Speed Limitation
 *   - 0: No Limitation
 *   - 1: Limitation obtained via GETMXDS CCC.
 */
#define I3C_BCR_MAX_DATA_SPEED_LIMIT			BIT(0)
#define I3C_BCR_IBI_REQUEST_CAPABLE			BIT(1)
#define I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE		BIT(2)
#define I3C_BCR_OFFLINE_CAPABLE				BIT(3)
#define I3C_BCR_VIRTUAL_TARGET				BIT(4)
#define I3C_BCR_ADV_CAPABILITIES			BIT(5)

#define I3C_BCR_DEVICE_ROLE_I3C_TARGET			0U
#define I3C_BCR_DEVICE_ROLE_I3C_CONTROLLER_CAPABLE	1U

#define I3C_BCR_DEVICE_ROLE_SHIFT			6U
#define I3C_BCR_DEVICE_ROLE_MASK			(0x03U << I3C_BCR_DEVICE_ROLE_SHIFT)

#define I3C_BCR_DEVICE_ROLE(bcr)			\
	(((bcr) & I3C_BCR_DEVICE_ROLE_MASK) >> I3C_BCR_DEVICE_ROLE_SHIFT)

/*
 * Legacy Virtual Register (LVR)
 * - LVR[7:5]: I2C device index:
 *   - 0: I2C device has a 50 ns spike filter where
 *        it is not affected by high frequency on SCL.
 *   - 1: I2C device does not have a 50 ns spike filter
 *        but can work with high frequency on SCL.
 *   - 2: I2C device does not have a 50 ns spike filter
 *        and cannot work with high frequency on SCL.
 * - LVR[4]: I2C mode indicator:
 *   - 0: FM+ mode
 *   - 1: FM mode
 * - LVR[3:0]: Reserved.
 */
#define I3C_DCR_I2C_FM_PLUS_MODE			0
#define I3C_DCR_I2C_FM_MODE				1

#define I3C_DCR_I2C_MODE_SHIFT				4
#define I3C_DCR_I2C_MODE_MASK				BIT(4)

#define I3C_DCR_I2C_MODE(dcr)				\
	(((mode) & I3C_DCR_I2C_MODE_MASK) >> I3C_DCR_I2C_MODE_SHIFT)

#define I3C_DCR_I2C_DEV_IDX_0				0
#define I3C_DCR_I2C_DEV_IDX_1				1
#define I3C_DCR_I2C_DEV_IDX_2				2

#define I3C_DCR_I2C_DEV_IDX_SHIFT			5
#define I3C_DCR_I2C_DEV_IDX_MASK			(0x07U << I3C_DCR_I2C_DEV_IDX_SHIFT)

#define I3C_DCR_I2C_DEV_IDX(dcr)			\
	(((dcr) & I3C_DCR_I2C_DEV_IDX_MASK) >> I3C_DCR_I2C_DEV_IDX_SHIFT)

/**
 * @brief I3C bus mode
 */
enum i3c_bus_mode {
	/** Only I3C devices are on the bus. */
	I3C_BUS_MODE_PURE,

	/**
	 * Both I3C and legacy I2C devices are on the bus.
	 * The I2C devices have 50ns spike filter on SCL.
	 */
	I3C_BUS_MODE_MIXED_FAST,

	/**
	 * Both I3C and legacy I2C devices are on the bus.
	 * The I2C devices do not have 50ns spike filter on SCL
	 * and can tolerate maximum SDR SCL clock frequency.
	 */
	I3C_BUS_MODE_MIXED_LIMITED,

	/**
	 * Both I3C and legacy I2C devices are on the bus.
	 * The I2C devices do not have 50ns spike filter on SCL
	 * but cannot tolerate maximum SDR SCL clock frequency.
	 */
	I3C_BUS_MODE_MIXED_SLOW,

	I3C_BUS_MODE_MAX = I3C_BUS_MODE_MIXED_SLOW,
	I3C_BUS_MODE_INVALID,
};

/**
 * @brief I2C bus speed under I3C bus.
 *
 * Only FM and FM+ modes are supported for I2C devices under I3C bus.
 */
enum i3c_i2c_speed_type {
	/** I2C FM mode */
	I3C_I2C_SPEED_FM,

	/** I2C FM+ mode */
	I3C_I2C_SPEED_FMPLUS,

	I3C_I2C_SPEED_MAX = I3C_I2C_SPEED_FMPLUS,
	I3C_I2C_SPEED_INVALID,
};

/**
 * @brief I3C data rate
 *
 * I3C data transfer rate defined by the I3C specification.
 */
enum i3c_data_rate {
	/** Single Data Rate messaging */
	I3C_DATA_RATE_SDR,

	/** High Data Rate - Double Data Rate messaging */
	I3C_DATA_RATE_HDR_DDR,

	/** High Data Rate - Ternary Symbol Legacy-inclusive-Bus */
	I3C_DATA_RATE_HDR_TSL,

	/** High Data Rate - Ternary Symbol for Pure Bus */
	I3C_DATA_RATE_HDR_TSP,

	/** High Data Rate - Bulk Transport */
	I3C_DATA_RATE_HDR_BT,

	I3C_DATA_RATE_MAX = I3C_DATA_RATE_HDR_BT,
	I3C_DATA_RATE_INVALID,
};

/**
 * @brief I3C SDR Controller Error Codes
 *
 * These are error codes defined by the I3C specification.
 *
 * @c I3C_ERROR_CE_UNKNOWN and @c I3C_ERROR_CE_NONE are not
 * official error codes according to the specification.
 * These are there simply to aid in error handling during
 * interactions with the I3C drivers and subsystem.
 */
enum i3c_sdr_controller_error_codes {
	/** Transaction after sending CCC */
	I3C_ERROR_CE0,

	/** Monitoring Error */
	I3C_ERROR_CE1,

	/** No response to broadcast address (0x7E) */
	I3C_ERROR_CE2,

	/** Failed Controller Handoff */
	I3C_ERROR_CE3,

	/** Unknown error (not official error code) */
	I3C_ERROR_CE_UNKNOWN,

	/** No error (not official error code) */
	I3C_ERROR_CE_NONE,

	I3C_ERROR_CE_MAX = I3C_ERROR_CE_UNKNOWN,
	I3C_ERROR_CE_INVALID,
};

/**
 * @brief I3C SDR Target Error Codes
 *
 * These are error codes defined by the I3C specification.
 *
 * @c I3C_ERROR_TE_UNKNOWN and @c I3C_ERROR_TE_NONE are not
 * official error codes according to the specification.
 * These are there simply to aid in error handling during
 * interactions with the I3C drivers and subsystem.
 */
enum i3c_sdr_target_error_codes {
	/**
	 * Invalid Broadcast Address or
	 * Dynamic Address after DA assignment
	 */
	I3C_ERROR_TE0,

	/** CCC Code */
	I3C_ERROR_TE1,

	/** Write Data */
	I3C_ERROR_TE2,

	/** Assigned Address during Dynamic Address Arbitration */
	I3C_ERROR_TE3,

	/** 0x7E/R missing after RESTART during Dynamic Address Arbitration */
	I3C_ERROR_TE4,

	/** Transaction after detecting CCC */
	I3C_ERROR_TE5,

	/** Monitoring Error */
	I3C_ERROR_TE6,

	/** Dead Bus Recovery */
	I3C_ERROR_DBR,

	/** Unknown error (not official error code) */
	I3C_ERROR_TE_UNKNOWN,

	/** No error (not official error code) */
	I3C_ERROR_TE_NONE,

	I3C_ERROR_TE_MAX = I3C_ERROR_TE_UNKNOWN,
	I3C_ERROR_TE_INVALID,
};

/*
 * I3C_MSG_* are I3C Message flags.
 */

/** Write message to I3C bus. */
#define I3C_MSG_WRITE			(0U << 0U)

/** Read message from I3C bus. */
#define I3C_MSG_READ			BIT(0)

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

/** Send STOP after this message. */
#define I3C_MSG_STOP			BIT(1)

/**
 * RESTART I3C transaction for this message.
 *
 * @note Not all I3C drivers have or require explicit support for this
 * feature. Some drivers require this be present on a read message
 * that follows a write, or vice-versa.  Some drivers will merge
 * adjacent fragments into a single transaction using this flag; some
 * will not.
 */
#define I3C_MSG_RESTART			BIT(2)

/** Transfer use HDR mode */
#define I3C_MSG_HDR			BIT(3)

/** Skip I3C broadcast header. Private Transfers only. */
#define I3C_MSG_NBCH			BIT(4)

/** I3C HDR Mode 0 */
#define I3C_MSG_HDR_MODE0		BIT(0)

/** I3C HDR Mode 1 */
#define I3C_MSG_HDR_MODE1		BIT(1)

/** I3C HDR Mode 2 */
#define I3C_MSG_HDR_MODE2		BIT(2)

/** I3C HDR Mode 3 */
#define I3C_MSG_HDR_MODE3		BIT(3)

/** I3C HDR Mode 4 */
#define I3C_MSG_HDR_MODE4		BIT(4)

/** I3C HDR Mode 5 */
#define I3C_MSG_HDR_MODE5		BIT(5)

/** I3C HDR Mode 6 */
#define I3C_MSG_HDR_MODE6		BIT(6)

/** I3C HDR Mode 7 */
#define I3C_MSG_HDR_MODE7		BIT(7)

/** I3C HDR-DDR (Double Data Rate) */
#define I3C_MSG_HDR_DDR			I3C_MSG_HDR_MODE0

/** I3C HDR-TSP (Ternary Symbol Pure-bus) */
#define I3C_MSG_HDR_TSP			I3C_MSG_HDR_MODE1

/** I3C HDR-TSL (Ternary Symbol Legacy-inclusive-bus) */
#define I3C_MSG_HDR_TSL			I3C_MSG_HDR_MODE2

/** I3C HDR-BT (Bulk Transport) */
#define I3C_MSG_HDR_BT			I3C_MSG_HDR_MODE3

/**
 * @brief One I3C Message.
 *
 * This defines one I3C message to transact on the I3C bus.
 *
 * @note Some of the configurations supported by this API may not be
 * supported by specific SoC I3C hardware implementations, in
 * particular features related to bus transactions intended to read or
 * write data from different buffers within a single transaction.
 * Invocations of i3c_transfer() may not indicate an error when an
 * unsupported configuration is encountered.  In some cases drivers
 * will generate separate transactions for each message fragment, with
 * or without presence of @ref I3C_MSG_RESTART in #flags.
 */
struct i3c_msg {
	/** Data buffer in bytes */
	uint8_t			*buf;

	/** Length of buffer in bytes */
	uint32_t		len;

	/** Flags for this message */
	uint8_t			flags;

	/**
	 * HDR mode (@c I3C_MSG_HDR_MODE*) for transfer
	 * if any @c I3C_MSG_HDR_* is set in @c flags.
	 *
	 * Use SDR mode if none is set.
	 */
	uint8_t			hdr_mode;
};

/**
 * @brief Type of configuration being passed to configure function.
 */
enum i3c_config_type {
	I3C_CONFIG_CONTROLLER,
	I3C_CONFIG_TARGET,
	I3C_CONFIG_CUSTOM,
};

/**
 * @brief Configuration parameters for I3C hardware to act as controller.
 */
struct i3c_config_controller {
	/**
	 * True if the controller is to be the secondary controller
	 * of the bus. False to be the primary controller.
	 */
	bool is_secondary;

	struct {
		/** SCL frequency (in Hz) for I3C transfers. */
		uint32_t i3c;

		/** SCL frequency (in Hz) for I2C transfers. */
		uint32_t i2c;
	} scl;

	/**
	 * Bit mask of supported HDR modes (0 - 7).
	 *
	 * This can be used to enable or disable HDR mode
	 * supported by the hardware at runtime.
	 */
	uint8_t supported_hdr;
};

/**
 * @brief Custom I3C configuration parameters.
 *
 * This can be used to configure the I3C hardware on parameters
 * not covered by @see i3c_config_controller or @see i3c_config_target.
 * Mostly used to configure vendor specific parameters of the I3C
 * hardware.
 */
struct i3c_config_custom {
	/** ID of the configuration parameter. */
	uint32_t id;

	union {
		/** Value of configuration parameter. */
		uintptr_t val;

		/**
		 * Pointer to configuration parameter.
		 *
		 * Mainly used to pointer to a struct that
		 * the device driver understands.
		 */
		void *ptr;
	};
};

/**
 * @cond INTERNAL_HIDDEN
 *
 * These are for internal use only, so skip these in
 * public documentation.
 */
struct i3c_device_desc;
struct i3c_device_id;
struct i3c_i2c_device_desc;
struct i3c_target_config;

__subsystem struct i3c_driver_api {
	/**
	 * For backward compatibility to I2C API.
	 *
	 * @see i2c_driver_api for more information.
	 *
	 * (DO NOT MOVE! Must be at the beginning.)
	 */
	struct i2c_driver_api i2c_api;

	/**
	 * Configure the I3C hardware.
	 *
	 * @see i3c_configure
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param type Type of configuration parameters being passed
	 *             in @p config.
	 * @param config Pointer to the configuration parameters.
	 *
	 * @return @see i3c_configure
	 */
	int (*configure)(const struct device *dev,
			 enum i3c_config_type type, void *config);

	/**
	 * Get configuration of the I3C hardware.
	 *
	 * @see i3c_config_get
	 *
	 * @param[in] dev Pointer to controller device driver instance.
	 * @param[in] type Type of configuration parameters being passed
	 *                 in @p config.
	 * @param[in, out] config Pointer to the configuration parameters.
	 *
	 * @return @see i3c_config_get
	 */
	int (*config_get)(const struct device *dev,
			  enum i3c_config_type type, void *config);

	/**
	 * Perform bus recovery
	 *
	 * Controller only API.
	 *
	 * @see i3c_recover_bus
	 *
	 * @param dev Pointer to controller device driver instance.
	 *
	 * @return @see i3c_recover_bus
	 */
	int (*recover_bus)(const struct device *dev);

	/**
	 * I3C Device Attach
	 *
	 * Optional API.
	 *
	 * @see i3c_attach_i3c_device
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 * @param addr Address to attach with
	 *
	 * @return @see i3c_attach_i3c_device
	 */
	int (*attach_i3c_device)(const struct device *dev,
			struct i3c_device_desc *target,
			uint8_t addr);

	/**
	 * I3C Address Update
	 *
	 * Optional API.
	 *
	 * @see i3c_reattach_i3c_device
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 * @param old_dyn_addr Old dynamic address
	 *
	 * @return @see i3c_reattach_i3c_device
	 */
	int (*reattach_i3c_device)(const struct device *dev,
			struct i3c_device_desc *target,
			uint8_t old_dyn_addr);

	/**
	 * I3C Device Detach
	 *
	 * Optional API.
	 *
	 * @see i3c_detach_i3c_device
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 *
	 * @return @see i3c_detach_i3c_device
	 */
	int (*detach_i3c_device)(const struct device *dev,
			struct i3c_device_desc *target);

	/**
	 * I2C Device Attach
	 *
	 * Optional API.
	 *
	 * @see i3c_attach_i2c_device
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 *
	 * @return @see i3c_attach_i2c_device
	 */
	int (*attach_i2c_device)(const struct device *dev,
			struct i3c_i2c_device_desc *target);

	/**
	 * I2C Device Detach
	 *
	 * Optional API.
	 *
	 * @see i3c_detach_i2c_device
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 *
	 * @return @see i3c_detach_i2c_device
	 */
	int (*detach_i2c_device)(const struct device *dev,
			struct i3c_i2c_device_desc *target);

	/**
	 * Perform Dynamic Address Assignment via ENTDAA.
	 *
	 * Controller only API.
	 *
	 * @see i3c_do_daa
	 *
	 * @param dev Pointer to controller device driver instance.
	 *
	 * @return @see i3c_do_daa
	 */
	int (*do_daa)(const struct device *dev);

	/**
	 * Send Common Command Code (CCC).
	 *
	 * Controller only API.
	 *
	 * @see i3c_do_ccc
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param payload Pointer to the CCC payload.
	 *
	 * @return @see i3c_do_ccc
	 */
	int (*do_ccc)(const struct device *dev,
		      struct i3c_ccc_payload *payload);

	/**
	 * Transfer messages in I3C mode.
	 *
	 * @see i3c_transfer
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 * @param msg Pointer to I3C messages.
	 * @param num_msgs Number of messages to transfer.
	 *
	 * @return @see i3c_transfer
	 */
	int (*i3c_xfers)(const struct device *dev,
			 struct i3c_device_desc *target,
			 struct i3c_msg *msgs,
			 uint8_t num_msgs);

	/**
	 * Find a registered I3C target device.
	 *
	 * Controller only API.
	 *
	 * This returns the I3C device descriptor of the I3C device
	 * matching the incoming @p id.
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param id Pointer to I3C device ID.
	 *
	 * @return @see i3c_device_find.
	 */
	struct i3c_device_desc *(*i3c_device_find)(const struct device *dev,
						   const struct i3c_device_id *id);

	/**
	 * Raise In-Band Interrupt (IBI).
	 *
	 * Target device only API.
	 *
	 * @see i3c_ibi_request
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param request Pointer to IBI request struct.
	 *
	 * @return @see i3c_ibi_request
	 */
	int (*ibi_raise)(const struct device *dev,
			 struct i3c_ibi *request);

	/**
	 * Enable receiving IBI from a target.
	 *
	 * Controller only API.
	 *
	 * @see i3c_ibi_enable
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 *
	 * @return @see i3c_ibi_enable
	 */
	int (*ibi_enable)(const struct device *dev,
			  struct i3c_device_desc *target);

	/**
	 * Disable receiving IBI from a target.
	 *
	 * Controller only API.
	 *
	 * @see i3c_ibi_disable
	 *
	 * @param dev Pointer to controller device driver instance.
	 * @param target Pointer to target device descriptor.
	 *
	 * @return @see i3c_ibi_disable
	 */
	int (*ibi_disable)(const struct device *dev,
			   struct i3c_device_desc *target);

	/**
	 * Register config as target device of a controller.
	 *
	 * This tells the controller to act as a target device
	 * on the I3C bus.
	 *
	 * Target device only API.
	 *
	 * @see i3c_target_register
	 *
	 * @param dev Pointer to the controller device driver instance.
	 * @param cfg I3C target device configuration
	 *
	 * @return @see i3c_target_register
	 */
	int (*target_register)(const struct device *dev,
			       struct i3c_target_config *cfg);

	/**
	 * Unregister config as target device of a controller.
	 *
	 * This tells the controller to stop acting as a target device
	 * on the I3C bus.
	 *
	 * Target device only API.
	 *
	 * @see i3c_target_unregister
	 *
	 * @param dev Pointer to the controller device driver instance.
	 * @param cfg I3C target device configuration
	 *
	 * @return @see i3c_target_unregister
	 */
	int (*target_unregister)(const struct device *dev,
				 struct i3c_target_config *cfg);

	/**
	 * Write to the TX FIFO
	 *
	 * This writes to the target tx fifo
	 *
	 * Target device only API.
	 *
	 * @see i3c_target_tx_write
	 *
	 * @param dev Pointer to the controller device driver instance.
	 * @param buf Pointer to the buffer
	 * @param len Length of the buffer
	 *
	 * @return @see i3c_target_tx_write
	 */
	int (*target_tx_write)(const struct device *dev,
				 uint8_t *buf, uint16_t len);
};

/**
 * @endcond
 */

/**
 * @brief Structure used for matching I3C devices.
 */
struct i3c_device_id {
	/** Device Provisioned ID */
	const uint64_t pid:48;
};

/**
 * @brief Structure initializer for i3c_device_id from PID
 *
 * This helper macro expands to a static initializer for a <tt>struct
 * i3c_device_id</tt> by populating the PID (Provisioned ID) field.
 *
 * @param pid Provisioned ID.
 */
#define I3C_DEVICE_ID(pid)						\
	{								\
		.pid = pid						\
	}

/**
 * @brief Structure describing a I3C target device.
 *
 * Instances of this are passed to the I3C controller device APIs,
 * for example:
 * - i3c_device_register() to tell the controller of a target device.
 * - i3c_transfers() to initiate data transfers between controller and
 *   target device.
 *
 * Fields @c bus, @c pid and @c static_addr must be initialized by
 * the module that implements the target device behavior prior to
 * passing the object reference to I3C controller device APIs.
 * @c static_addr can be zero if target device does not have static
 * address.
 *
 * Field @c node should not be initialized or modified manually.
 */
struct i3c_device_desc {
	/** Private, do not modify */
	sys_snode_t node;

	/** I3C bus to which this target device is attached */
	const struct device * const bus;

	/** Device driver instance of the I3C device */
	const struct device * const dev;

	/** Device Provisioned ID */
	const uint64_t pid:48;

	/**
	 * Static address for this target device.
	 *
	 * 0 if static address is not being used, and only dynamic
	 * address is used. This means that the target device must
	 * go through ENTDAA (Dynamic Address Assignment) to get
	 * a dynamic address before it can communicate with
	 * the controller. This means SETAASA and SETDASA CCC
	 * cannot be used to set dynamic address on the target
	 * device (as both are to tell target device to use static
	 * address as dynamic address).
	 */
	const uint8_t static_addr;

	/**
	 * Initial dynamic address.
	 *
	 * This is specified in the device tree property "assigned-address"
	 * to indicate the desired dynamic address during address
	 * assignment (SETDASA and ENTDAA).
	 *
	 * 0 if there is no preference.
	 */
	const uint8_t init_dynamic_addr;

	/**
	 * Dynamic Address for this target device used for communication.
	 *
	 * This is to be set by the controller driver in one of
	 * the following situations:
	 * - During Dynamic Address Assignment (during ENTDAA)
	 * - Reset Dynamic Address Assignment (RSTDAA)
	 * - Set All Addresses to Static Addresses (SETAASA)
	 * - Set New Dynamic Address (SETNEWDA)
	 * - Set Dynamic Address from Static Address (SETDASA)
	 *
	 * 0 if address has not been assigned.
	 */
	uint8_t dynamic_addr;

#if defined(CONFIG_I3C_USE_GROUP_ADDR) || defined(__DOXYGEN__)
	/**
	 * Group address for this target device. Set during:
	 * - Reset Group Address(es) (RSTGRPA)
	 * - Set Group Address (SETGRPA)
	 *
	 * 0 if group address has not been assigned.
	 */
	uint8_t group_addr;
#endif /* CONFIG_I3C_USE_GROUP_ADDR */

	/**
	 * Bus Characteristic Register (BCR)
	 * - BCR[7:6]: Device Role
	 *   - 0: I3C Target
	 *   - 1: I3C Controller capable
	 *   - 2: Reserved
	 *   - 3: Reserved
	 * - BCR[5]: Advanced Capabilities
	 *   - 0: Does not support optional advanced capabilities.
	 *   - 1: Supports optional advanced capabilities which
	 *        can be viewed via GETCAPS CCC.
	 * - BCR[4}: Virtual Target Support
	 *   - 0: Is not a virtual target.
	 *   - 1: Is a virtual target.
	 * - BCR[3]: Offline Capable
	 *   - 0: Will always response to I3C commands.
	 *   - 1: Will not always response to I3C commands.
	 * - BCR[2]: IBI Payload
	 *   - 0: No data bytes following the accepted IBI.
	 *   - 1: One data byte (MDB, Mandatory Data Byte) follows
	 *        the accepted IBI. Additional data bytes may also
	 *        follows.
	 * - BCR[1]: IBI Request Capable
	 *   - 0: Not capable
	 *   - 1: Capable
	 * - BCR[0]: Max Data Speed Limitation
	 *   - 0: No Limitation
	 *   - 1: Limitation obtained via GETMXDS CCC.
	 */
	uint8_t bcr;

	/**
	 * Device Characteristic Register (DCR)
	 *
	 * Describes the type of device. Refer to official documentation
	 * on what this number means.
	 */
	uint8_t dcr;

	struct {
		/** Maximum Read Speed */
		uint8_t maxrd;

		/** Maximum Write Speed */
		uint8_t maxwr;

		/** Maximum Read turnaround time in microseconds. */
		uint32_t max_read_turnaround;
	} data_speed;

	struct {
		/** Maximum Read Length */
		uint16_t mrl;

		/** Maximum Write Length */
		uint16_t mwl;

		/** Maximum IBI Payload Size. Valid only if BCR[2] is 1. */
		uint8_t max_ibi;
	} data_length;

	/** Private data by the controller to aid in transactions. Do not modify. */
	void *controller_priv;

#if defined(CONFIG_I3C_USE_IBI) || defined(__DOXYGEN__)
	/**
	 * In-Band Interrupt (IBI) callback.
	 */
	i3c_target_ibi_cb_t ibi_cb;
#endif /* CONFIG_I3C_USE_IBI */
};

/**
 * @brief Structure describing a I2C device on I3C bus.
 *
 * Instances of this are passed to the I3C controller device APIs,
 * for example:
 * () i3c_i2c_device_register() to tell the controller of an I2C device.
 * () i3c_i2c_transfers() to initiate data transfers between controller
 *    and I2C device.
 *
 * Fields other than @c node must be initialized by the module that
 * implements the device behavior prior to passing the object
 * reference to I3C controller device APIs.
 */
struct i3c_i2c_device_desc {
	/** Private, do not modify */
	sys_snode_t node;

	/** I3C bus to which this I2C device is attached */
	const struct device *bus;

	/** Static address for this I2C device. */
	const uint16_t addr;

	/**
	 * Legacy Virtual Register (LVR)
	 * - LVR[7:5]: I2C device index:
	 *   - 0: I2C device has a 50 ns spike filter where
	 *        it is not affected by high frequency on SCL.
	 *   - 1: I2C device does not have a 50 ns spike filter
	 *        but can work with high frequency on SCL.
	 *   - 2: I2C device does not have a 50 ns spike filter
	 *        and cannot work with high frequency on SCL.
	 * - LVR[4]: I2C mode indicator:
	 *   - 0: FM+ mode
	 *   - 1: FM mode
	 * - LVR[3:0]: Reserved.
	 */
	const uint8_t lvr;

	/** Private data by the controller to aid in transactions. Do not modify. */
	void *controller_priv;
};

/**
 * @brief Structure for describing attached devices for a controller.
 *
 * This contains slists of attached I3C and I2C devices.
 *
 * This is a helper struct that can be used by controller device
 * driver to aid in device management.
 */
struct i3c_dev_attached_list {
	/**
	 * Address slots:
	 * - Aid in dynamic address assignment.
	 * - Quick way to find out if a target address is
	 *   a I3C or I2C device.
	 */
	struct i3c_addr_slots addr_slots;

	struct {
		/**
		 * Linked list of attached I3C devices.
		 */
		sys_slist_t i3c;

		/**
		 * Linked list of attached I2C devices.
		 */
		sys_slist_t i2c;
	} devices;
};

/**
 * @brief Structure for describing known devices for a controller.
 *
 * This contains arrays of known I3C and I2C devices.
 *
 * This is a helper struct that can be used by controller device
 * driver to aid in device management.
 */
struct i3c_dev_list {
	/**
	 * Pointer to array of known I3C devices.
	 */
	struct i3c_device_desc * const i3c;

	/**
	 * Pointer to array of known I2C devices.
	 */
	struct i3c_i2c_device_desc * const i2c;

	/**
	 * Number of I3C devices in array.
	 */
	const uint8_t num_i3c;

	/**
	 * Number of I2C devices in array.
	 */
	const uint8_t num_i2c;
};

/**
 * This structure is common to all I3C drivers and is expected to be
 * the first element in the object pointed to by the config field
 * in the device structure.
 */
struct i3c_driver_config {
	/** I3C/I2C device list struct. */
	struct i3c_dev_list dev_list;
};

/**
 * This structure is common to all I3C drivers and is expected to be the first
 * element in the driver's struct driver_data declaration.
 */
struct i3c_driver_data {
	/** Controller Configuration */
	struct i3c_config_controller ctrl_config;

	/** Attached I3C/I2C devices and addresses */
	struct i3c_dev_attached_list attached_dev;
};

/**
 * @brief Find a I3C target device descriptor by ID.
 *
 * This finds the I3C target device descriptor in the device list
 * matching the provided ID struct (@p id).
 *
 * @param dev_list Pointer to the device list struct.
 * @param id Pointer to I3C device ID struct.
 *
 * @return Pointer the the I3C target device descriptor, or
 *         NULL if none is found.
 */
struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list,
					  const struct i3c_device_id *id);

/**
 * @brief Find a I3C target device descriptor by dynamic address.
 *
 * This finds the I3C target device descriptor in the attached
 * device list matching the dynamic address (@p addr)
 *
 * @param dev_list Pointer to the device list struct.
 * @param addr Dynamic address to be matched.
 *
 * @return Pointer the the I3C target device descriptor, or
 *         NULL if none is found.
 */
struct i3c_device_desc *i3c_dev_list_i3c_addr_find(struct i3c_dev_attached_list *dev_list,
						   uint8_t addr);

/**
 * @brief Find a I2C target device descriptor by address.
 *
 * This finds the I2C target device descriptor in the attached
 * device list matching the address (@p addr)
 *
 * @param dev_list Pointer to the device list struct.
 * @param addr Address to be matched.
 *
 * @return Pointer the the I2C target device descriptor, or
 *         NULL if none is found.
 */
struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(struct i3c_dev_attached_list *dev_list,
							   uint16_t addr);

/**
 * @brief Helper function to find the default address an i3c device is attached with
 *
 * This is a helper function to find the default address the
 * device will be loaded with. This could be either it's static
 * address, a requested dynamic address, or just a dynamic address
 * that is available
 * @param[in] target The pointer of the device descriptor
 * @param[out] addr Address to be assigned to target device.
 *
 * @retval 0 if successful.
 * @retval -EINVAL if the expected default address is already in use
 */
int i3c_determine_default_addr(struct i3c_device_desc *target, uint8_t *addr);

/**
 * @brief Helper function to find a usable address during ENTDAA.
 *
 * This is a helper function to find a usable address during
 * Dynamic Address Assignment. Given the PID (@p pid), it will
 * search through the device list for the matching device
 * descriptor. If the device descriptor indicates that there is
 * a preferred address (i.e. assigned-address in device tree,
 * @see i3c_device_desc::init_dynamic_addr), this preferred
 * address will be returned if this address is still available.
 * If it is not available, another free address will be returned.
 *
 * If @p must_match is true, the PID (@p pid) must match
 * one of the device in the device list.
 *
 * If @p must_match is false, this will return an arbitrary
 * address. This is useful when not all devices are described in
 * device tree. Or else, the DAA process cannot proceed since
 * there is no address to be assigned.
 *
 * If @p assigned_okay is true, it will return the same address
 * already assigned to the device
 * (@see i3c_device_desc::dynamic_addr). If no address has been
 * assigned, it behaves as if @p assigned_okay is false.
 * This is useful for assigning the same address to the same
 * device (for example, hot-join after device coming back from
 * suspend).
 *
 * If @p assigned_okay is false, the device cannot have an address
 * assigned already (that @see i3c_device_desc::dynamic_addr is not
 * zero). This is mainly used during the initial DAA.
 *
 * @param[in] addr_slots Pointer to address slots struct.
 * @param[in] dev_list Pointer to the device list struct.
 * @param[in] pid Provisioned ID of device to be assigned address.
 * @param[in] must_match True if PID must match devices in
 *			 the device list. False otherwise.
 * @param[in] assigned_okay True if it is okay to return the
 *                          address already assigned to the target
 *                          matching the PID (@p pid).
 * @param[out] target Store the pointer of the device descriptor
 *                    if it matches the incoming PID (@p pid).
 * @param[out] addr Address to be assigned to target device.
 *
 * @retval 0 if successful.
 * @retval -ENODEV if no device matches the PID (@p pid) in
 *                 the device list and @p must_match is true.
 * @retval -EINVAL if the device matching PID (@p pid) already
 *                 has an address assigned or invalid function
 *                 arguments.
 */
int i3c_dev_list_daa_addr_helper(struct i3c_addr_slots *addr_slots,
				 const struct i3c_dev_list *dev_list,
				 uint64_t pid, bool must_match,
				 bool assigned_okay,
				 struct i3c_device_desc **target,
				 uint8_t *addr);

/**
 * @brief Configure the I3C hardware.
 *
 * @param dev Pointer to controller device driver instance.
 * @param type Type of configuration parameters being passed
 *             in @p config.
 * @param config Pointer to the configuration parameters.
 *
 * @retval 0 If successful.
 * @retval -EINVAL If invalid configure parameters.
 * @retval -EIO General Input/Output errors.
 * @retval -ENOSYS If not implemented.
 */
static inline int i3c_configure(const struct device *dev,
				enum i3c_config_type type, void *config)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->configure(dev, type, config);
}

/**
 * @brief Get configuration of the I3C hardware.
 *
 * This provides a way to get the current configuration of the I3C hardware.
 *
 * This can return cached config or probed hardware parameters, but it has to
 * be up to date with current configuration.
 *
 * @param[in] dev Pointer to controller device driver instance.
 * @param[in] type Type of configuration parameters being passed
 *                 in @p config.
 * @param[in,out] config Pointer to the configuration parameters.
 *
 * Note that if @p type is @c I3C_CONFIG_CUSTOM, @p config must contain
 * the ID of the parameter to be retrieved.
 *
 * @retval 0 If successful.
 * @retval -EIO General Input/Output errors.
 * @retval -ENOSYS If not implemented.
 */
static inline int i3c_config_get(const struct device *dev,
				 enum i3c_config_type type, void *config)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->config_get(dev, type, config);
}

/**
 * @brief Attempt bus recovery on the I3C bus.
 *
 * This routine asks the controller to attempt bus recovery.
 *
 * @retval 0 If successful.
 * @retval -EBUSY If bus recovery fails.
 * @retval -EIO General input / output error.
 * @retval -ENOSYS Bus recovery is not supported by the controller driver.
 */
static inline int i3c_recover_bus(const struct device *dev)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->recover_bus(dev);
}

/**
 * @brief Attach an I3C device
 *
 * Called to attach a I3C device to the addresses. This is
 * typically called before a SETDASA or ENTDAA to reserve
 * the addresses. This will also call the optional api to
 * update any registers within the driver if implemented.
 *
 * @warning
 * Use cases involving multiple writers to the i3c/i2c devices must prevent
 * concurrent write operations, either by preventing all writers from
 * being preempted or by using a mutex to govern writes to the i3c/i2c devices.
 *
 * @param target Pointer to the target device descriptor
 *
 * @retval 0 If successful.
 * @retval -EINVAL If address is not available or if the device
 *     has already been attached before
 */
int i3c_attach_i3c_device(struct i3c_device_desc *target);

/**
 * @brief Reattach I3C device
 *
 * called after every time an I3C device has its address
 * changed. It can be because the device has been powered
 * down and has lost its address, or it can happen when a
 * device had a static address and has been assigned a
 * dynamic address with SETDASA or a dynamic address has
 * been updated with SETNEWDA. This will also call the
 * optional api to update any registers within the driver
 * if implemented.
 *
 * @warning
 * Use cases involving multiple writers to the i3c/i2c devices must prevent
 * concurrent write operations, either by preventing all writers from
 * being preempted or by using a mutex to govern writes to the i3c/i2c devices.
 *
 * @param target Pointer to the target device descriptor
 * @param old_dyn_addr The old dynamic address of target device, 0 if
 *            there was no old dynamic address
 *
 * @retval 0 If successful.
 * @retval -EINVAL If address is not available
 */
int i3c_reattach_i3c_device(struct i3c_device_desc *target, uint8_t old_dyn_addr);

/**
 * @brief Detach I3C Device
 *
 * called to remove an I3C device and to free up the address
 * that it used. If it's dynamic address was not set, then it
 * assumed that SETDASA failed and will free it's static addr.
 * This will also call the optional api to update any registers
 * within the driver if implemented.
 *
 * @warning
 * Use cases involving multiple writers to the i3c/i2c devices must prevent
 * concurrent write operations, either by preventing all writers from
 * being preempted or by using a mutex to govern writes to the i3c/i2c devices.
 *
 * @param target Pointer to the target device descriptor
 *
 * @retval 0 If successful.
 * @retval -EINVAL If device is already detached
 */
int i3c_detach_i3c_device(struct i3c_device_desc *target);

/**
 * @brief Attach an I2C device
 *
 * Called to attach a I2C device to the addresses. This will
 * also call the optional api to update any registers within
 * the driver if implemented.
 *
 * @warning
 * Use cases involving multiple writers to the i3c/i2c devices must prevent
 * concurrent write operations, either by preventing all writers from
 * being preempted or by using a mutex to govern writes to the i3c/i2c devices.
 *
 * @param target Pointer to the target device descriptor
 *
 * @retval 0 If successful.
 * @retval -EINVAL If address is not available or if the device
 *     has already been attached before
 */
int i3c_attach_i2c_device(struct i3c_i2c_device_desc *target);

/**
 * @brief Detach I2C Device
 *
 * called to remove an I2C device and to free up the address
 * that it used. This will also call the optional api to
 * update any registers within the driver if implemented.
 *
 * @warning
 * Use cases involving multiple writers to the i3c/i2c devices must prevent
 * concurrent write operations, either by preventing all writers from
 * being preempted or by using a mutex to govern writes to the i3c/i2c devices.
 *
 * @param target Pointer to the target device descriptor
 *
 * @retval 0 If successful.
 * @retval -EINVAL If device is already detached
 */
int i3c_detach_i2c_device(struct i3c_i2c_device_desc *target);

/**
 * @brief Perform Dynamic Address Assignment on the I3C bus.
 *
 * This routine asks the controller to perform dynamic address assignment
 * where the controller belongs. Only the active controller of the bus
 * should do this.
 *
 * @note For controller driver implementation, the controller should perform
 * SETDASA to allow static addresses to be the dynamic addresses before
 * actually doing ENTDAA.
 *
 * @param dev Pointer to the device structure for the controller driver
 *            instance.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 * @retval -ENODEV If a provisioned ID does not match to any target devices
 *                 in the registered device list.
 * @retval -ENOSPC No more free addresses can be assigned to target.
 * @retval -ENOSYS Dynamic address assignment is not supported by
 *                 the controller driver.
 */
static inline int i3c_do_daa(const struct device *dev)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->do_daa(dev);
}

/**
 * @brief Send CCC to the bus.
 *
 * @param dev Pointer to the device structure for the controller driver
 *            instance.
 * @param payload Pointer to the structure describing the CCC payload.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General Input / output error.
 * @retval -EINVAL Invalid valid set in the payload structure.
 * @retval -ENOSYS Not implemented.
 */
__syscall int i3c_do_ccc(const struct device *dev,
			 struct i3c_ccc_payload *payload);

static inline int z_impl_i3c_do_ccc(const struct device *dev,
				    struct i3c_ccc_payload *payload)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->do_ccc(dev, payload);
}

/**
 * @brief Perform data transfer from the controller to a I3C target device.
 *
 * This routine provides a generic interface to perform data transfer
 * to a target device synchronously. Use i3c_read()/i3c_write()
 * for simple read or write.
 *
 * The array of message @a msgs must not be NULL.  The number of
 * message @a num_msgs may be zero, in which case no transfer occurs.
 *
 * @note Not all scatter/gather transactions can be supported by all
 * drivers.  As an example, a gather write (multiple consecutive
 * `i3c_msg` buffers all configured for `I3C_MSG_WRITE`) may be packed
 * into a single transaction by some drivers, but others may emit each
 * fragment as a distinct write transaction, which will not produce
 * the same behavior.  See the documentation of `struct i3c_msg` for
 * limitations on support for multi-message bus transactions.
 *
 * @param target I3C target device descriptor.
 * @param msgs Array of messages to transfer.
 * @param num_msgs Number of messages to transfer.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
__syscall int i3c_transfer(struct i3c_device_desc *target,
			   struct i3c_msg *msgs, uint8_t num_msgs);

static inline int z_impl_i3c_transfer(struct i3c_device_desc *target,
				      struct i3c_msg *msgs, uint8_t num_msgs)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)target->bus->api;

	return api->i3c_xfers(target->bus, target, msgs, num_msgs);
}

/**
 * Find a registered I3C target device.
 *
 * Controller only API.
 *
 * This returns the I3C device descriptor of the I3C device
 * matching the incoming @p id.
 *
 * @param dev Pointer to controller device driver instance.
 * @param id Pointer to I3C device ID.
 *
 * @return Pointer to I3C device descriptor, or NULL if
 *         no I3C device found matching incoming @p id.
 */
static inline
struct i3c_device_desc *i3c_device_find(const struct device *dev,
					const struct i3c_device_id *id)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->i3c_device_find(dev, id);
}

/**
 * @brief Raise an In-Band Interrupt (IBI).
 *
 * This raises an In-Band Interrupt (IBI) to the active controller.
 *
 * @param dev Pointer to controller device driver instance.
 * @param request Pointer to the IBI request struct.
 *
 * @retval 0 if operation is successful.
 * @retval -EIO General input / output error.
 */
static inline int i3c_ibi_raise(const struct device *dev,
				struct i3c_ibi *request)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)dev->api;

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

	return api->ibi_raise(dev, request);
}

/**
 * @brief Enable IBI of a target device.
 *
 * This enables IBI of a target device where the IBI has already been
 * request.
 *
 * @param target I3C target device descriptor.
 *
 * @retval 0 If successful.
 * @retval -EIO General Input / output error.
 * @retval -ENOMEM If these is no more empty entries in
 *                 the controller's IBI table (if the controller
 *                 uses such table).
 */
static inline int i3c_ibi_enable(struct i3c_device_desc *target)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)target->bus->api;

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

	return api->ibi_enable(target->bus, target);
}

/**
 * @brief Disable IBI of a target device.
 *
 * This enables IBI of a target device where the IBI has already been
 * request.
 *
 * @param target I3C target device descriptor.
 *
 * @retval 0 If successful.
 * @retval -EIO General Input / output error.
 * @retval -ENODEV If IBI is not previously enabled for @p target.
 */
static inline int i3c_ibi_disable(struct i3c_device_desc *target)
{
	const struct i3c_driver_api *api =
		(const struct i3c_driver_api *)target->bus->api;

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

	return api->ibi_disable(target->bus, target);
}

/**
 * @brief Check if target's IBI has payload.
 *
 * This reads the BCR from the device descriptor struct to determine
 * whether IBI from device has payload.
 *
 * Note that BCR must have been obtained from device and
 * @see i3c_device_desc::bcr must be set.
 *
 * @return True if IBI has payload, false otherwise.
 */
static inline int i3c_ibi_has_payload(struct i3c_device_desc *target)
{
	return (target->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE)
		== I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE;
}

/**
 * @brief Check if device is IBI capable.
 *
 * This reads the BCR from the device descriptor struct to determine
 * whether device is capable of IBI.
 *
 * Note that BCR must have been obtained from device and
 * @see i3c_device_desc::bcr must be set.
 *
 * @return True if IBI has payload, false otherwise.
 */
static inline int i3c_device_is_ibi_capable(struct i3c_device_desc *target)
{
	return (target->bcr & I3C_BCR_IBI_REQUEST_CAPABLE)
		== I3C_BCR_IBI_REQUEST_CAPABLE;
}

/**
 * @brief Write a set amount of data to an I3C target device.
 *
 * This routine writes a set amount of data synchronously.
 *
 * @param target I3C target device descriptor.
 * @param buf Memory pool from which the data is transferred.
 * @param num_bytes Number of bytes to write.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_write(struct i3c_device_desc *target,
			    const uint8_t *buf, uint32_t num_bytes)
{
	struct i3c_msg msg;

	msg.buf = (uint8_t *)buf;
	msg.len = num_bytes;
	msg.flags = I3C_MSG_WRITE | I3C_MSG_STOP;

	return i3c_transfer(target, &msg, 1);
}

/**
 * @brief Read a set amount of data from an I3C target device.
 *
 * This routine reads a set amount of data synchronously.
 *
 * @param target I3C target device descriptor.
 * @param buf Memory pool that stores the retrieved data.
 * @param num_bytes Number of bytes to read.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_read(struct i3c_device_desc *target,
			   uint8_t *buf, uint32_t num_bytes)
{
	struct i3c_msg msg;

	msg.buf = buf;
	msg.len = num_bytes;
	msg.flags = I3C_MSG_READ | I3C_MSG_STOP;

	return i3c_transfer(target, &msg, 1);
}

/**
 * @brief Write then read data from an I3C target device.
 *
 * This supports the common operation "this is what I want", "now give
 * it to me" transaction pair through a combined write-then-read bus
 * transaction.
 *
 * @param target I3C target device descriptor.
 * @param write_buf Pointer to the data to be written
 * @param num_write Number of bytes to write
 * @param read_buf Pointer to storage for read data
 * @param num_read Number of bytes to read
 *
 * @retval 0 if successful
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_write_read(struct i3c_device_desc *target,
				 const void *write_buf, size_t num_write,
				 void *read_buf, size_t num_read)
{
	struct i3c_msg msg[2];

	msg[0].buf = (uint8_t *)write_buf;
	msg[0].len = num_write;
	msg[0].flags = I3C_MSG_WRITE;

	msg[1].buf = (uint8_t *)read_buf;
	msg[1].len = num_read;
	msg[1].flags = I3C_MSG_RESTART | I3C_MSG_READ | I3C_MSG_STOP;

	return i3c_transfer(target, msg, 2);
}

/**
 * @brief Read multiple bytes from an internal address of an I3C target device.
 *
 * This routine reads multiple bytes from an internal address of an
 * I3C target device synchronously.
 *
 * Instances of this may be replaced by i3c_write_read().
 *
 * @param target I3C target device descriptor,
 * @param start_addr Internal address from which the data is being read.
 * @param buf Memory pool that stores the retrieved data.
 * @param num_bytes Number of bytes being read.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_burst_read(struct i3c_device_desc *target,
				 uint8_t start_addr,
				 uint8_t *buf,
				 uint32_t num_bytes)
{
	return i3c_write_read(target,
			      &start_addr, sizeof(start_addr),
			      buf, num_bytes);
}

/**
 * @brief Write multiple bytes to an internal address of an I3C target device.
 *
 * This routine writes multiple bytes to an internal address of an
 * I3C target device synchronously.
 *
 * @warning The combined write synthesized by this API may not be
 * supported on all I3C devices.  Uses of this API may be made more
 * portable by replacing them with calls to i3c_write() passing a
 * buffer containing the combined address and data.
 *
 * @param target I3C target device descriptor.
 * @param start_addr Internal address to which the data is being written.
 * @param buf Memory pool from which the data is transferred.
 * @param num_bytes Number of bytes being written.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_burst_write(struct i3c_device_desc *target,
				  uint8_t start_addr,
				  const uint8_t *buf,
				  uint32_t num_bytes)
{
	struct i3c_msg msg[2];

	msg[0].buf = &start_addr;
	msg[0].len = 1U;
	msg[0].flags = I3C_MSG_WRITE;

	msg[1].buf = (uint8_t *)buf;
	msg[1].len = num_bytes;
	msg[1].flags = I3C_MSG_WRITE | I3C_MSG_STOP;

	return i3c_transfer(target, msg, 2);
}

/**
 * @brief Read internal register of an I3C target device.
 *
 * This routine reads the value of an 8-bit internal register of an I3C target
 * device synchronously.
 *
 * @param target I3C target device descriptor.
 * @param reg_addr Address of the internal register being read.
 * @param value Memory pool that stores the retrieved register value.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_reg_read_byte(struct i3c_device_desc *target,
				    uint8_t reg_addr, uint8_t *value)
{
	return i3c_write_read(target,
			      &reg_addr, sizeof(reg_addr),
			      value, sizeof(*value));
}

/**
 * @brief Write internal register of an I3C target device.
 *
 * This routine writes a value to an 8-bit internal register of an I3C target
 * device synchronously.
 *
 * @note This function internally combines the register and value into
 * a single bus transaction.
 *
 * @param target I3C target device descriptor.
 * @param reg_addr Address of the internal register being written.
 * @param value Value to be written to internal register.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_reg_write_byte(struct i3c_device_desc *target,
				     uint8_t reg_addr, uint8_t value)
{
	uint8_t tx_buf[2] = {reg_addr, value};

	return i3c_write(target, tx_buf, 2);
}

/**
 * @brief Update internal register of an I3C target device.
 *
 * This routine updates the value of a set of bits from an 8-bit internal
 * register of an I3C target device synchronously.
 *
 * @note If the calculated new register value matches the value that
 * was read this function will not generate a write operation.
 *
 * @param target I3C target device descriptor.
 * @param reg_addr Address of the internal register being updated.
 * @param mask Bitmask for updating internal register.
 * @param value Value for updating internal register.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 */
static inline int i3c_reg_update_byte(struct i3c_device_desc *target,
				      uint8_t reg_addr, uint8_t mask,
				      uint8_t value)
{
	uint8_t old_value, new_value;
	int rc;

	rc = i3c_reg_read_byte(target, reg_addr, &old_value);
	if (rc != 0) {
		return rc;
	}

	new_value = (old_value & ~mask) | (value & mask);
	if (new_value == old_value) {
		return 0;
	}

	return i3c_reg_write_byte(target, reg_addr, new_value);
}

/**
 * @brief Dump out an I3C message
 *
 * Dumps out a list of I3C messages. For any that are writes (W), the data is
 * displayed in hex.
 *
 * It looks something like this (with name "testing"):
 *
 * @code
 * D: I3C msg: testing, addr=56
 * D:    W len=01:
 * D: contents:
 * D: 06                      |.
 * D:    W len=0e:
 * D: contents:
 * D: 00 01 02 03 04 05 06 07 |........
 * D: 08 09 0a 0b 0c 0d       |......
 * @endcode
 *
 * @param name Name of this dump, displayed at the top.
 * @param msgs Array of messages to dump.
 * @param num_msgs Number of messages to dump.
 * @param target I3C target device descriptor.
 */
void i3c_dump_msgs(const char *name, const struct i3c_msg *msgs,
		   uint8_t num_msgs, struct i3c_device_desc *target);

/**
 * @brief Generic helper function to perform bus initialization.
 *
 * @param dev Pointer to controller device driver instance.
 * @param i3c_dev_list Pointer to I3C device list.
 *
 * @retval 0 If successful.
 * @retval -EBUSY Bus is busy.
 * @retval -EIO General input / output error.
 * @retval -ENODEV If a provisioned ID does not match to any target devices
 *                 in the registered device list.
 * @retval -ENOSPC No more free addresses can be assigned to target.
 * @retval -ENOSYS Dynamic address assignment is not supported by
 *                 the controller driver.
 */
int i3c_bus_init(const struct device *dev,
		 const struct i3c_dev_list *i3c_dev_list);

/**
 * @brief Get basic information from device and update device descriptor.
 *
 * This retrieves some basic information:
 *   * Bus Characteristics Register (GETBCR)
 *   * Device Characteristics Register (GETDCR)
 *   * Max Read Length (GETMRL)
 *   * Max Write Length (GETMWL)
 * from the device and update the corresponding fields of the device
 * descriptor.
 *
 * This only updates the field(s) in device descriptor
 * only if CCC operations succeed.
 *
 * @param[in,out] target I3C target device descriptor.
 *
 * @retval 0 if successful.
 * @retval -EIO General Input/Output error.
 */
int i3c_device_basic_info_get(struct i3c_device_desc *target);

/*
 * This needs to be after declaration of struct i3c_driver_api,
 * or else compiler complains about undefined type inside
 * the static inline API wrappers.
 */
#include <zephyr/drivers/i3c/target_device.h>

#ifdef __cplusplus
}
#endif

/**
 * @}
 */

#include <syscalls/i3c.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_I3C_H_ */