// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2021 Intel Corporation */
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "icp_qat_fw_init_admin.h"
#include "adf_heartbeat.h"
#include "adf_cfg.h"

static const
u8 const_tab[ADF_CONST_TABLE_SIZE] __aligned(ADF_CONST_TABLE_SIZE) = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
0x54, 0x32, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab,
0x89, 0x98, 0xba, 0xdc, 0xfe, 0x10, 0x32, 0x54, 0x76, 0xc3, 0xd2, 0xe1, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x05, 0x9e,
0xd8, 0x36, 0x7c, 0xd5, 0x07, 0x30, 0x70, 0xdd, 0x17, 0xf7, 0x0e, 0x59, 0x39,
0xff, 0xc0, 0x0b, 0x31, 0x68, 0x58, 0x15, 0x11, 0x64, 0xf9, 0x8f, 0xa7, 0xbe,
0xfa, 0x4f, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae,
0x85, 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, 0x51, 0x0e, 0x52, 0x7f,
0x9b, 0x05, 0x68, 0x8c, 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19, 0x05,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcb, 0xbb, 0x9d, 0x5d, 0xc1, 0x05, 0x9e, 0xd8, 0x62, 0x9a, 0x29,
0x2a, 0x36, 0x7c, 0xd5, 0x07, 0x91, 0x59, 0x01, 0x5a, 0x30, 0x70, 0xdd, 0x17,
0x15, 0x2f, 0xec, 0xd8, 0xf7, 0x0e, 0x59, 0x39, 0x67, 0x33, 0x26, 0x67, 0xff,
0xc0, 0x0b, 0x31, 0x8e, 0xb4, 0x4a, 0x87, 0x68, 0x58, 0x15, 0x11, 0xdb, 0x0c,
0x2e, 0x0d, 0x64, 0xf9, 0x8f, 0xa7, 0x47, 0xb5, 0x48, 0x1d, 0xbe, 0xfa, 0x4f,
0xa4, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb,
0x67, 0xae, 0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94,
0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, 0x0e, 0x52,
0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f,
0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13,
0x7e, 0x21, 0x79, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define ADF_ADMIN_POLL_MIN_DELAY_US 20
#define ADF_ADMIN_POLL_MAX_DELAY_US (5 * 1000 * 1000)

int adf_put_admin_msg_sync(struct adf_accel_dev *accel_dev, u32 ae,
			   void *in, void *out)
{
	struct adf_admin_comms *admin = accel_dev->admin;
	int offset = ae * ADF_ADMINMSG_LEN * 2;
	void __iomem *mailbox = admin->mailbox_addr;
	int mb_offset = ae * ADF_MAILBOX_STRIDE;
	unsigned long delay = ADF_ADMIN_POLL_MIN_DELAY_US;
	int received;
	struct icp_qat_fw_init_admin_req *request = in;

	mutex_lock(&admin->lock);

	if (ADF_CSR_RD(mailbox, mb_offset) == 1) {
		mutex_unlock(&admin->lock);
		return -EAGAIN;
	}

	memcpy((u8 *)(admin->virt_addr) + offset, in, ADF_ADMINMSG_LEN);
	ADF_CSR_WR(mailbox, mb_offset, 1);
	received = 0;

	do {
		usleep_range(delay, delay * 2);
		delay *= 2;
		if (ADF_CSR_RD(mailbox, mb_offset) == 0) {
			received = 1;
			break;
		}
	} while (delay < ADF_ADMIN_POLL_MAX_DELAY_US);

	if (received) {
		/* Response received from admin message, we can now
		 * make response data available in "out" parameter.
		 */
		memcpy(out, (u8 *)(admin->virt_addr) + offset +
		       ADF_ADMINMSG_LEN, ADF_ADMINMSG_LEN);
	} else {
		dev_err(&GET_DEV(accel_dev),
			"Failed to send admin msg %d to accelerator %d\n",
			request->cmd_id, ae);
	}

	mutex_unlock(&admin->lock);
	return received ? 0 : -EFAULT;
}
EXPORT_SYMBOL_GPL(adf_put_admin_msg_sync);

#ifdef QAT_UIO
static inline int adf_set_dc_ibuf(struct adf_accel_dev *accel_dev,
				  struct icp_qat_fw_init_admin_req *req)
{
	char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = {0};
	unsigned long ibuf_size = 0;

	if (!adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
				     ADF_INTER_BUF_SIZE, val)) {
		if (kstrtoul(val, 0, &ibuf_size))
			return -EFAULT;
	}

	if (ibuf_size != 32 && ibuf_size != 64)
		ibuf_size = 64;

	req->ibuf_size_in_kb = ibuf_size;

	return 0;
}

#endif


int adf_send_admin(struct adf_accel_dev *accel_dev,
		   struct icp_qat_fw_init_admin_req *req,
		   struct icp_qat_fw_init_admin_resp *resp,
		   u32 ae_mask)
{
	int i;
	u32 mask;

	for (i = 0, mask = ae_mask; mask; i++, mask >>= 1) {
		if (!(mask & 1))
			continue;
		if (adf_put_admin_msg_sync(accel_dev, i, req, resp) ||
		    resp->status)
			return -EFAULT;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(adf_send_admin);

static int adf_init_me(struct adf_accel_dev *accel_dev)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp resp;
	struct adf_hw_device_data *hw_device = accel_dev->hw_device;
	u32 ae_mask = hw_device->ae_mask;

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));
	req.cmd_id = ICP_QAT_FW_INIT_ME;

#ifdef QAT_UIO
	if (adf_set_dc_ibuf(accel_dev, &req))
		return -EFAULT;
#endif

	if (accel_dev->aram_info) {
		req.init_cfg_sz = sizeof(*accel_dev->aram_info);
		req.init_cfg_ptr = accel_dev->admin->aram_map_phys_addr;
	}
	if (adf_send_admin(accel_dev, &req, &resp, ae_mask))
		return -EFAULT;

	return 0;
}

static int adf_get_dc_capabilities(struct adf_accel_dev *accel_dev,
				   u32 *capabilities)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp resp;
	u32 ae_mask = 1;

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));
	req.cmd_id = ICP_QAT_FW_COMP_CAPABILITY_GET;

	if (adf_send_admin(accel_dev, &req, &resp, ae_mask))
		return -EFAULT;

	*capabilities = resp.extended_features;

	return 0;
}

static int adf_set_heartbeat_timer(struct adf_accel_dev *accel_dev)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp resp;
	struct adf_hw_device_data *hw_device = accel_dev->hw_device;
	u32 ae_mask = hw_device->ae_mask;

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));
	req.cmd_id = ICP_QAT_FW_HEARTBEAT_TIMER_SET;
	req.init_cfg_ptr = accel_dev->admin->phy_hb_addr;
	if (adf_get_hb_timer(accel_dev, &req.heartbeat_ticks))
		return -EINVAL;

	if (adf_send_admin(accel_dev, &req, &resp, ae_mask))
		return -EFAULT;

	return 0;
}

static int adf_set_fw_constants(struct adf_accel_dev *accel_dev)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp resp;
	struct adf_hw_device_data *hw_device = accel_dev->hw_device;
	u32 ae_mask = hw_device->ae_mask;

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));
	req.cmd_id = ICP_QAT_FW_CONSTANTS_CFG;

	req.init_cfg_sz = ADF_CONST_TABLE_SIZE;
	req.init_cfg_ptr = accel_dev->admin->const_tbl_addr;

	if (adf_send_admin(accel_dev, &req, &resp, ae_mask))
		return -EFAULT;

	return 0;
}

static int adf_get_fw_status(struct adf_accel_dev *accel_dev,
			     u8 *major, u8 *minor, u8 *patch)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp resp;
	u32 ae_mask = 1;

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));
	req.cmd_id = ICP_QAT_FW_STATUS_GET;

	if (adf_send_admin(accel_dev, &req, &resp, ae_mask))
		return -EFAULT;

	*major = resp.version_major_num;
	*minor = resp.version_minor_num;
	*patch = resp.version_patch_num;

	return 0;
}

int adf_get_fw_timestamp(struct adf_accel_dev *accel_dev, u64 *timestamp)
{
	struct icp_qat_fw_init_admin_req req;
	struct icp_qat_fw_init_admin_resp rsp;
	unsigned int ae_mask = 1;

	if (!accel_dev || !timestamp)
		return -EFAULT;

	memset(&req, 0, sizeof(req));
	req.cmd_id = ICP_QAT_FW_TIMER_GET;

	if (adf_send_admin(accel_dev, &req, &rsp, ae_mask))
		return -EFAULT;

	*timestamp = rsp.timestamp;
	return 0;
}

int adf_get_fw_pke_stats(struct adf_accel_dev *accel_dev,
			 u64 *suc_count,
			 u64 *unsuc_count)
{
	struct icp_qat_fw_init_admin_req req = {0};
	struct icp_qat_fw_init_admin_resp resp = {0};
	unsigned long sym_ae_msk = 0;
	u8 sym_ae_msk_size = 0;
	u8 i = 0;

	if (!suc_count || !unsuc_count)
		return -EFAULT;

	sym_ae_msk = accel_dev->au_info->sym_ae_msk;
	sym_ae_msk_size = sizeof(accel_dev->au_info->sym_ae_msk) *
		BITS_PER_BYTE;

	req.cmd_id = ICP_QAT_FW_PKE_REPLAY_STATS_GET;
	for_each_set_bit(i, &sym_ae_msk, sym_ae_msk_size) {
		memset(&resp,
		       0,
		       sizeof(struct icp_qat_fw_init_admin_resp));
		if (adf_put_admin_msg_sync(accel_dev, i, &req, &resp) ||
		    resp.status) {
			return -EIO;
		}
		*suc_count += resp.successful_count;
		*unsuc_count += resp.unsuccessful_count;
	}
	return 0;
}
EXPORT_SYMBOL_GPL(adf_get_fw_pke_stats);

/**
 * adf_send_admin_init() - Function sends init message to FW
 * @accel_dev: Pointer to acceleration device.
 *
 * Function sends admin init message to the FW
 *
 * Return: 0 on success, error code otherwise.
 */
int adf_send_admin_init(struct adf_accel_dev *accel_dev)
{
	int ret;
	u32 dc_capabilities = 0;
	unsigned int storage_enabled = 0;

	ret = adf_init_me(accel_dev);
	if (ret)
		return ret;

	if (GET_HW_DATA(accel_dev)->query_storage_cap) {
		ret = adf_get_dc_capabilities(accel_dev, &dc_capabilities);
		if (ret)
			return ret;
		accel_dev->hw_device->extended_dc_capabilities =
			dc_capabilities;
		if (accel_dev->hw_device->extended_dc_capabilities &
			ICP_ACCEL_CAPABILITIES_DC_CHAINING) {
			accel_dev->hw_device->accel_capabilities_mask |=
				ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC;
		}
	} else {
		ret = GET_HW_DATA(accel_dev)->
			get_storage_enabled(accel_dev,
					    &storage_enabled);
		if (ret)
			return ret;
	}
	ret = adf_set_heartbeat_timer(accel_dev);
	if (ret == -EINVAL)
		return ret;
	else if (ret)
		dev_err(&GET_DEV(accel_dev), "Heartbeat is not supported\n");

	adf_get_fw_status(accel_dev,
			  &accel_dev->fw_versions.fw_version_major,
			  &accel_dev->fw_versions.fw_version_minor,
			  &accel_dev->fw_versions.fw_version_patch);
	dev_dbg(&GET_DEV(accel_dev), "FW version: %d.%d.%d\n",
		accel_dev->fw_versions.fw_version_major,
		accel_dev->fw_versions.fw_version_minor,
		accel_dev->fw_versions.fw_version_patch);

	return adf_set_fw_constants(accel_dev);
}
EXPORT_SYMBOL_GPL(adf_send_admin_init);

int adf_init_admin_comms(struct adf_accel_dev *accel_dev)
{
	struct adf_admin_comms *admin;
	struct adf_hw_device_data *hw_data = accel_dev->hw_device;
	struct adf_bar *pmisc =
		&GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
	void __iomem *csr = pmisc->virt_addr;
	struct admin_info admin_csrs_info;
	unsigned int mailbox_offset, adminmsg_u, adminmsg_l;
	void __iomem *mailbox;
	u64 reg_val;

	admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL,
			     dev_to_node(&GET_DEV(accel_dev)));
	if (!admin)
		return -ENOMEM;
	admin->virt_addr = dma_alloc_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
					      &admin->phy_addr, GFP_KERNEL);
	if (!admin->virt_addr) {
		dev_err(&GET_DEV(accel_dev), "Failed to allocate dma buffer\n");
		kfree(admin);
		return -ENOMEM;
	}

	/* DMA constant tables */
	admin->dma_tbl_addr = dma_alloc_coherent(&GET_DEV(accel_dev),
						 PAGE_SIZE,
						 &admin->const_tbl_addr,
						 GFP_KERNEL);
	if (!admin->dma_tbl_addr) {
		dev_err(&GET_DEV(accel_dev), "Failed to allocate const_tbl\n");
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->virt_addr, admin->phy_addr);
		kfree(admin);
		return -ENOMEM;
	}

	memcpy(admin->dma_tbl_addr, const_tab, sizeof(const_tab));

	/* DMA ARAM address map */
	if (accel_dev->aram_info) {
		admin->aram_map_phys_addr =
			 dma_map_single(&GET_DEV(accel_dev),
					accel_dev->aram_info,
					sizeof(*accel_dev->aram_info),
					DMA_TO_DEVICE);

		if (unlikely(
			dma_mapping_error(&GET_DEV(accel_dev),
					  admin->aram_map_phys_addr))) {
			dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
					  admin->virt_addr, admin->phy_addr);
			dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
					  admin->dma_tbl_addr,
					  admin->const_tbl_addr);
			kfree(admin);
			return -ENOMEM;
		}
	}

	admin->virt_hb_addr = dma_alloc_coherent(&GET_DEV(accel_dev),
						 PAGE_SIZE,
						 &admin->phy_hb_addr,
						 GFP_KERNEL);
	if (!admin->virt_hb_addr) {
		dev_err(&GET_DEV(accel_dev), "Failed to allocate hb stats\n");
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->virt_addr, admin->phy_addr);
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->dma_tbl_addr, admin->const_tbl_addr);
		kfree(admin);
		return -ENOMEM;
	}

	hw_data->get_admin_info(&admin_csrs_info);
	mailbox_offset = admin_csrs_info.mailbox_offset;
	mailbox = (void __iomem *)((uintptr_t)csr + mailbox_offset);
	adminmsg_u = admin_csrs_info.admin_msg_ur;
	adminmsg_l = admin_csrs_info.admin_msg_lr;
	reg_val = (u64)admin->phy_addr;
	dev_dbg(&GET_DEV(accel_dev),
		"DMA table address 0x%llx\n",
		admin->aram_map_phys_addr);
	ADF_CSR_WR(csr, adminmsg_u, reg_val >> 32);
	ADF_CSR_WR(csr, adminmsg_l, reg_val);
	mutex_init(&admin->lock);
	admin->mailbox_addr = mailbox;
	accel_dev->admin = admin;
	return 0;
}
EXPORT_SYMBOL_GPL(adf_init_admin_comms);

void adf_exit_admin_comms(struct adf_accel_dev *accel_dev)
{
	struct adf_admin_comms *admin = accel_dev->admin;

	if (!admin)
		return;

	if (admin->virt_addr)
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->virt_addr, admin->phy_addr);

	if (admin->virt_hb_addr)
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->virt_hb_addr, admin->phy_hb_addr);

	if (admin->dma_tbl_addr)
		dma_free_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
				  admin->dma_tbl_addr, admin->const_tbl_addr);
	admin->dma_tbl_addr = NULL;

	mutex_destroy(&admin->lock);
	kfree(admin);
	accel_dev->admin = NULL;
}
EXPORT_SYMBOL_GPL(adf_exit_admin_comms);
