/*
 * Copyright (c) 2023 Intel Corporation
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef _SEDI_DRIVER_IPC_H_
#define _SEDI_DRIVER_IPC_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "sedi_driver_common.h"

/*
 * struct sedi_ipc
 * define IPC interface ID
 * \ingroup sedi_driver_ipc
 */
typedef enum {
	SEDI_IPC_HOST = 0,
	SEDI_IPC_CSME,
	SEDI_IPC_PMC,
	SEDI_IPC_NUM
} sedi_ipc_t;

/****** IPC Event *****/

/*
 * SEDI_IPC_EVENT_MSG_IN
 * Received an incoming ipc message
 * \ingroup sedi_driver_ipc
 */
#define SEDI_IPC_EVENT_MSG_IN (1UL << 0)

/*
 * SEDI_IPC_EVENT_MSG_PEER_ACKED
 * An ipc busy bit is cleared by peer
 * \ingroup sedi_driver_ipc
 */
#define SEDI_IPC_EVENT_MSG_PEER_ACKED (1UL << 1)

/*
 * SEDI_IPC_EVENT_MSG_OUT
 * An ipc message is received by peer
 * \ingroup sedi_driver_ipc
 */
#define SEDI_IPC_EVENT_MSG_OUT (1UL << 2)

/*
 * SEDI_IPC_EVENT_CSR_ACK
 * Receive a CSR ack from peer after writing CSR
 * \ingroup sedi_driver_ipc
 */
#define SEDI_IPC_EVENT_CSR_ACK (1UL << 3)

/****** IPC Capability *****/

/*
 * struct sedi_ipc_capabilities_t
 * define IPC Driver Capabilities.
 * \ingroup sedi_driver_ipc
 */
typedef struct {
	uint32_t is_available : 1;
	uint32_t reserved : 31;
} sedi_ipc_capabilities_t;

/****** IPC Driver helper definitions *****/

#define IPC_PROTOCOL_BOOT 0
#define IPC_PROTOCOL_HECI 1
#define IPC_PROTOCOL_MCTP 2
#define IPC_PROTOCOL_MNG 3

#define IPC_DRBL_BUSY_BIT 31
#define IPC_DATA_LEN_MAX  128

#define IPC_HEADER_LENGTH_MASK (0x03FF)
#define IPC_HEADER_PROTOCOL_MASK (0x0F)
#define IPC_HEADER_MNG_CMD_MASK (0x0F)

#define IPC_HEADER_LENGTH_OFFSET 0
#define IPC_HEADER_PROTOCOL_OFFSET 10
#define IPC_HEADER_MNG_CMD_OFFSET 16
#define IPC_DRBL_BUSY_OFFS 31

#define IPC_HEADER_GET_LENGTH(drbl_reg)        \
	(((drbl_reg) >> IPC_HEADER_LENGTH_OFFSET) & IPC_HEADER_LENGTH_MASK)
#define IPC_HEADER_GET_PROTOCOL(drbl_reg) \
	(((drbl_reg) >> IPC_HEADER_PROTOCOL_OFFSET) & IPC_HEADER_PROTOCOL_MASK)
#define IPC_HEADER_GET_MNG_CMD(drbl_reg) \
	(((drbl_reg) >> IPC_HEADER_MNG_CMD_OFFSET) & IPC_HEADER_MNG_CMD_MASK)
#define IPC_IS_BUSY(drbl) ((drbl) & BIT(IPC_DRBL_BUSY_OFFS))
#define IPC_SET_BUSY(drbl) ((drbl) | BIT(IPC_DRBL_BUSY_OFFS))

#define IPC_BUILD_DRBL(length, protocol)             \
	((1 << IPC_DRBL_BUSY_OFFS)                    \
	 | ((protocol) << IPC_HEADER_PROTOCOL_OFFSET) \
	 | ((length) << IPC_HEADER_LENGTH_OFFSET))

#define IPC_BUILD_MNG_DRBL(cmd, length)                              \
	(((1) << IPC_DRBL_BUSY_OFFS)                          \
	 | ((IPC_PROTOCOL_MNG) << IPC_HEADER_PROTOCOL_OFFSET) \
	 | ((cmd) << IPC_HEADER_MNG_CMD_OFFSET)               \
	 | ((length) << IPC_HEADER_LENGTH_OFFSET))

/* CSR bit definition */
#define IPC_CSR_NO_MSG			0
#define IPC_CSR_RESET_ENTRY		BIT(0)
#define IPC_CSR_RESET_EXIT		BIT(1)
#define IPC_CSR_QUERY			BIT(2)
#define IPC_CSR_ASSERT_VALID		BIT(3)
#define IPC_CSR_ACKED_VALID		BIT(4)
#define IPC_CSR_DEASSERT_VALID		BIT(5)
#define IPC_CSR_SRAM_CLAIM		BIT(31)

/****** IPC Driver API *****/

/*
 * ipc_event_handler IPC Event Handler Callback
 * typedef sedi_ipc_event_cb_t
 * Callback function type for signal ipc event.
 * param[in] event: event type.
 * return    void
 */
typedef void (*sedi_ipc_event_cb_t)(IN sedi_ipc_t device, IN uint32_t event,
				    INOUT void *params);

/*
 * Get the ipc driver's API version.
 * return the version of current ipc driver's API
 */
sedi_driver_version_t sedi_ipc_get_version(void);

/*
 * Get the device's capabilities.
 * param[in] ipc_device: ipc device id
 * param[inout]  the capabilities of specific ipc device
 * return return status
 */
int32_t sedi_ipc_get_capabilities(IN sedi_ipc_t ipc_device,
				  INOUT sedi_ipc_capabilities_t *cap);
/*
 * write CSR message to peer.
 * param[in] ipc_device: ipc device id
 * param[in] csr: the csr content to sent
 * return return status
 */
int32_t sedi_ipc_write_csr(IN sedi_ipc_t ipc_device, IN uint32_t csr);

/*
 * read CSR message from peer.
 * param[in] ipc_device: ipc device id
 * param[out] csr: the pointer storing the csr msg
 * return return status
 */
int32_t sedi_ipc_read_csr(IN sedi_ipc_t ipc_device, OUT uint32_t *csr);

/*
 * Initialize the device
 * param[in] ipc_device: ipc device id
 * param[in] cb: the callback function which can receive device's events.
 * param[in] user_params: user params, will be the last input of callback
 * return  return_status
 */
int32_t sedi_ipc_init(IN sedi_ipc_t ipc_device, IN sedi_ipc_event_cb_t cb,
		      INOUT void *user_params);

/*
 * Uninitialize the device
 * param[in] ipc_device: ipc device id
 * return  return_status
 */
int32_t sedi_ipc_uninit(IN sedi_ipc_t ipc_device);

/*
 * Set the device's power
 * param[in] ipc_device: ipc device id
 * param[in] state: the power state to be set to the device
 * return  return_status
 */
int32_t sedi_ipc_set_power(IN sedi_ipc_t ipc_device,
			   IN sedi_power_state_t state);

/*
 * Write data to IPC message fifo
 * param[in] ipc_device: ipc device id
 * param[in] msg: point to memory area where data is stored
 * param[in] size: the length of data buffer
 * return  return_status
 */
int32_t sedi_ipc_write_msg(IN sedi_ipc_t ipc_device, IN uint8_t *msg,
			   IN int32_t size);

/*
 * Write IPC doorbell register
 * param[in] ipc_device: ipc device id
 * param[in] doorbell: the value of doorbell
 * return  return_status
 */
int32_t sedi_ipc_write_dbl(IN sedi_ipc_t ipc_device, IN uint32_t doorbell);

/*
 * Read data from IPC message fifo
 * param[in] ipc_device: ipc device id
 * param[out] msg: point to memory area where data will be stored
 * param[in] size: the length of data buffer
 * return  return_status
 */
int32_t sedi_ipc_read_msg(IN sedi_ipc_t ipc_device, OUT uint8_t *msg,
			  IN int32_t size);

/*
 * Read IPC doorbell register
 * param[in] ipc_device: ipc device id
 * param[out] doorbel: point to the value of doorbell
 * return  return_status
 */
int32_t sedi_ipc_read_dbl(IN sedi_ipc_t ipc_device, OUT uint32_t *doorbell);

/*
 * send ack IPC message
 * param[in] ipc_device: ipc device id
 * param[in] msg: the ack msg
 * param[in] size: the length of data buffer
 * return  return_status
 */
int32_t sedi_ipc_send_ack_msg(IN sedi_ipc_t ipc_device, IN uint8_t *msg,
			      IN int32_t size);

/*
 * send ack IPC doorbell register
 * param[in] ipc_device: ipc device id
 * param[in] doorbell: the ack doorbell
 * return  return_status
 */
int32_t sedi_ipc_send_ack_drbl(IN sedi_ipc_t ipc_device, IN uint32_t ack);

/*
 * read peer ack IPC message
 * param[in] ipc_device: ipc device id
 * param[out] doorbell: the buffer to store ack msg
 * param[in] size: the length of data buffer
 * return  return_status
 */
int32_t sedi_ipc_read_ack_msg(IN sedi_ipc_t ipc_device, OUT uint8_t *msg,
			      IN int32_t size);

/*
 * read peer ack IPC doorbell register
 * param[in] ipc_device: ipc device id
 * param[out] doorbell: the ack doorbell
 * return  return_status
 */
int32_t sedi_ipc_read_ack_drbl(IN sedi_ipc_t ipc_device, OUT uint32_t *ack);

#ifdef __cplusplus
}
#endif

#endif /* _SEDI_DRIVER_IPC_H_*/