/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright(c) 2007-2022 Intel Corporation */
#include "adf_cfg_instance.h"
#include "adf_cfg_device.h"
#include "adf_cfg_section.h"

static bool
adf_cfg_is_svc_enabled(struct adf_accel_dev *accel_dev, const u8 svc)
{
	int ring_pair_index = 0;
	u8 serv_type = NA;
	struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);

	for (ring_pair_index = 0; ring_pair_index < ADF_CFG_NUM_SERVICES;
	     ring_pair_index++) {
		serv_type =
		    GET_SRV_TYPE(hw_data->ring_to_svc_map, ring_pair_index);
		if (serv_type == svc)
			return true;
	}
	return false;
}

static int
adf_cfg_set_core_number_for_instance(struct adf_accel_dev *accel_dev,
				     const char *sec_name,
				     const char *inst_name,
				     int process_num,
				     unsigned long *core_number)
{
	char *core_val = NULL;
	char *pos = NULL;
	char **tokens = NULL;
	int token_index = 0;
	int core_arr_index = 0;
	int i = 0;
	int ret = EFAULT;
	unsigned long *core_num_arr = NULL;
	unsigned long core_num;
	unsigned long start, end;

	/* do memory allocation */
	core_val =
	    malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	tokens = malloc(sizeof(char *) * ADF_CFG_MAX_TOKENS,
			M_QAT,
			M_WAITOK | M_ZERO);

	for (i = 0; i < ADF_CFG_MAX_TOKENS; i++) {
		tokens[i] =
		    malloc(ADF_CFG_MAX_TOKEN_LEN, M_QAT, M_WAITOK | M_ZERO);
	}

	core_num_arr = malloc(sizeof(unsigned long) * ADF_CFG_MAX_CORE_NUM,
			      M_QAT,
			      M_WAITOK | M_ZERO);

	/* parse the core_val */
	ret = EFAULT;
	if (adf_cfg_get_param_value(accel_dev, sec_name, inst_name, core_val))
		goto failed;

	pos = strchr(core_val, ',');
	while (pos) {
		pos[0] = '\0';
		strlcpy(tokens[token_index++], core_val, ADF_CFG_MAX_TOKEN_LEN);
		strlcpy(core_val, pos + 1, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
		pos = strchr(core_val, ',');
		if (!pos)
			strlcpy(tokens[token_index++],
				core_val,
				ADF_CFG_MAX_VAL_LEN_IN_BYTES);
	}

	/* in case there is only N-M */
	if (token_index == 0)
		strlcpy(tokens[token_index++],
			core_val,
			ADF_CFG_MAX_VAL_LEN_IN_BYTES);

	/* parse the tokens such as N-M */
	for (i = 0; i < token_index; i++) {
		pos = strchr(tokens[i], '-');
		if (pos) {
			pos[0] = '\0';
			ret = compat_strtoul(tokens[i], 10, &start);
			if (ret)
				goto failed;
			ret = compat_strtoul(pos + 1, 10, &end);
			if (ret)
				goto failed;
			if (start > end) {
				ret = EFAULT;
				goto failed;
			}
			for (core_num = start; core_num < end + 1; core_num++)
				core_num_arr[core_arr_index++] = core_num;
		} else {
			ret = compat_strtoul(tokens[i], 10, &core_num);
			if (ret)
				goto failed;
			core_num_arr[core_arr_index++] = core_num;
		}
	}

	if (core_arr_index == 0) {
		ret = compat_strtoul(core_val, 10, &core_num);
		if (ret)
			goto failed;
		else
			core_num_arr[core_arr_index++] = core_num;
	}

	*core_number = core_num_arr[process_num % core_arr_index];
	ret = 0;
failed:
	free(core_val, M_QAT);
	if (tokens) {
		for (i = 0; i < ADF_CFG_MAX_TOKENS; i++)
			free(tokens[i], M_QAT);
		free(tokens, M_QAT);
	}
	free(core_num_arr, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Get core number failed with error %d\n",
			      ret);
	return ret;
}

static int
adf_cfg_set_value(struct adf_accel_dev *accel_dev,
		  const char *sec,
		  const char *key,
		  unsigned long *value)
{
	char *val = NULL;
	int ret = EFAULT;

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	if (adf_cfg_get_param_value(accel_dev, sec, key, val))
		goto out;

	/* as the key type can be either ADF_DEC or ADF_HEX */
	if (compat_strtoul(val, 10, value) && compat_strtoul(val, 16, value))
		goto out;

	ret = 0;
out:
	free(val, M_QAT);
	return ret;
}

static void
adf_cfg_add_cy_inst_info(struct adf_accel_dev *accel_dev,
			 struct adf_cfg_instance *crypto_inst,
			 const char *derived_sec,
			 int inst_index)
{
	char *key = NULL;
	unsigned long bank_number = 0;
	unsigned long ring_number = 0;
	unsigned long asym_req = 0;
	unsigned long sym_req = 0;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_BANK_NUM_FORMAT,
		 inst_index);
	bank_number = crypto_inst->bundle;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&bank_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_ASYM_TX_FORMAT,
		 inst_index);
	ring_number = crypto_inst->asym_tx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_SYM_TX_FORMAT,
		 inst_index);
	ring_number = crypto_inst->sym_tx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_ASYM_RX_FORMAT,
		 inst_index);
	ring_number = crypto_inst->asym_rx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_SYM_RX_FORMAT,
		 inst_index);
	ring_number = crypto_inst->sym_rx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	strlcpy(key, ADF_CY_RING_ASYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &asym_req))
		asym_req = ADF_CFG_DEF_CY_RING_ASYM_SIZE;

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_RING_ASYM_SIZE_FORMAT,
		 inst_index);
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&asym_req, ADF_DEC);

	strlcpy(key, ADF_CY_RING_SYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &sym_req))
		sym_req = ADF_CFG_DEF_CY_RING_SYM_SIZE;

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_RING_SYM_SIZE_FORMAT,
		 inst_index);
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&sym_req, ADF_DEC);

	free(key, M_QAT);
}

static void
adf_cfg_add_dc_inst_info(struct adf_accel_dev *accel_dev,
			 struct adf_cfg_instance *dc_inst,
			 const char *derived_sec,
			 int inst_index)
{
	char *key = NULL;
	unsigned long bank_number = 0;
	unsigned long ring_number = 0;
	unsigned long dc_req = 0;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	snprintf(key, ADF_CFG_MAX_STR_LEN, ADF_DC_BANK_NUM_FORMAT, inst_index);
	bank_number = dc_inst->bundle;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&bank_number, ADF_DEC);

	snprintf(key, ADF_CFG_MAX_STR_LEN, ADF_DC_TX_FORMAT, inst_index);
	ring_number = dc_inst->dc_tx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key, ADF_CFG_MAX_STR_LEN, ADF_DC_RX_FORMAT, inst_index);
	ring_number = dc_inst->dc_rx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	strlcpy(key, ADF_DC_RING_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &dc_req))
		dc_req = ADF_CFG_DEF_DC_RING_SIZE;

	snprintf(key, ADF_CFG_MAX_STR_LEN, ADF_DC_RING_SIZE_FORMAT, inst_index);
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&dc_req, ADF_DEC);

	free(key, M_QAT);
}

static void
adf_cfg_add_asym_inst_info(struct adf_accel_dev *accel_dev,
			   struct adf_cfg_instance *asym_inst,
			   const char *derived_sec,
			   int inst_index)
{
	char *key = NULL;
	unsigned long bank_number = 0;
	unsigned long ring_number = 0;
	unsigned long asym_req = 0;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	if (adf_cy_inst_cross_banks(accel_dev))
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_ASYM_BANK_NUM_FORMAT,
			 inst_index);
	else
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_BANK_NUM_FORMAT,
			 inst_index);
	bank_number = asym_inst->bundle;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&bank_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_ASYM_TX_FORMAT,
		 inst_index);
	ring_number = asym_inst->asym_tx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_ASYM_RX_FORMAT,
		 inst_index);
	ring_number = asym_inst->asym_rx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	strlcpy(key, ADF_CY_RING_ASYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &asym_req))
		asym_req = ADF_CFG_DEF_CY_RING_ASYM_SIZE;

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_RING_ASYM_SIZE_FORMAT,
		 inst_index);
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&asym_req, ADF_DEC);

	free(key, M_QAT);
}

static void
adf_cfg_add_sym_inst_info(struct adf_accel_dev *accel_dev,
			  struct adf_cfg_instance *sym_inst,
			  const char *derived_sec,
			  int inst_index)
{
	char *key = NULL;
	unsigned long bank_number = 0;
	unsigned long ring_number = 0;
	unsigned long sym_req = 0;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	if (adf_cy_inst_cross_banks(accel_dev))
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_SYM_BANK_NUM_FORMAT,
			 inst_index);
	else
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_BANK_NUM_FORMAT,
			 inst_index);

	bank_number = sym_inst->bundle;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&bank_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_SYM_TX_FORMAT,
		 inst_index);
	ring_number = sym_inst->sym_tx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_SYM_RX_FORMAT,
		 inst_index);
	ring_number = sym_inst->sym_rx;
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&ring_number, ADF_DEC);

	strlcpy(key, ADF_CY_RING_SYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &sym_req))
		sym_req = ADF_CFG_DEF_CY_RING_SYM_SIZE;

	snprintf(key,
		 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
		 ADF_CY_RING_SYM_SIZE_FORMAT,
		 inst_index);
	adf_cfg_add_key_value_param(
	    accel_dev, derived_sec, key, (void *)&sym_req, ADF_DEC);

	free(key, M_QAT);
}

static int
adf_cfg_section_copy(struct adf_accel_dev *accel_dev,
		     const char *processed_sec,
		     const char *derived_sec)
{
	unsigned long val = 0;
	struct list_head *list;
	struct adf_cfg_section *sec_process =
	    adf_cfg_sec_find(accel_dev, processed_sec);
	if (!sec_process)
		return EFAULT;

	list_for_each(list, &sec_process->param_head)
	{
		struct adf_cfg_key_val *ptr =
		    list_entry(list, struct adf_cfg_key_val, list);

		/*
		 * ignore CoreAffinity since it will be generated later, and
		 * there is no need to keep NumProcesses and LimitDevAccess.
		 */
		if (strstr(ptr->key, ADF_ETRMGR_CORE_AFFINITY) ||
		    strstr(ptr->key, ADF_NUM_PROCESSES) ||
		    strstr(ptr->key, ADF_LIMIT_DEV_ACCESS))
			continue;

		if (ptr->type == ADF_DEC) {
			if (!compat_strtoul(ptr->val, 10, &val))
				adf_cfg_add_key_value_param(accel_dev,
							    derived_sec,
							    ptr->key,
							    (void *)&val,
							    ptr->type);
		} else if (ptr->type == ADF_STR) {
			adf_cfg_add_key_value_param(accel_dev,
						    derived_sec,
						    ptr->key,
						    (void *)ptr->val,
						    ptr->type);
		} else if (ptr->type == ADF_HEX) {
			if (!compat_strtoul(ptr->val, 16, &val))
				adf_cfg_add_key_value_param(accel_dev,
							    derived_sec,
							    ptr->key,
							    (void *)val,
							    ptr->type);
		}
	}
	return 0;
}

static int
adf_cfg_create_rings_entries_for_cy_inst(struct adf_accel_dev *accel_dev,
					 const char *processed_sec,
					 const char *derived_sec,
					 int process_num,
					 enum adf_cfg_service_type serv_type)
{
	int i = 0;
	int ret = EFAULT;
	unsigned long num_inst = 0, num_dc_inst = 0;
	unsigned long core_number = 0;
	unsigned long polling_mode = 0;
	struct adf_cfg_instance *crypto_inst = NULL;

	char *key = NULL;
	char *val = NULL;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	snprintf(key, ADF_CFG_MAX_KEY_LEN_IN_BYTES, ADF_SERVICES_ENABLED);
	if (adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, key, val))
		goto failed;
	if ((!strncmp(val, ADF_CFG_CY, ADF_CFG_MAX_VAL_LEN_IN_BYTES)) ||
	    (!strncmp(val, ADF_CFG_ASYM, ADF_CFG_MAX_VAL_LEN_IN_BYTES)) ||
	    (!strncmp(val, ADF_CFG_SYM, ADF_CFG_MAX_VAL_LEN_IN_BYTES))) {
		strlcpy(key, ADF_NUM_DC, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
		if (adf_cfg_set_value(
			accel_dev, processed_sec, key, &num_dc_inst))
			goto failed;
		if (num_dc_inst > 0) {
			device_printf(
			    GET_DEV(accel_dev),
			    "NumDcInstances > 0,when CY only is enabled\n");
			goto failed;
		}
	}
	ret = EFAULT;

	strlcpy(key, ADF_NUM_CY, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, processed_sec, key, &num_inst))
		goto failed;

	crypto_inst = malloc(sizeof(*crypto_inst), M_QAT, M_WAITOK | M_ZERO);

	for (i = 0; i < num_inst; i++) {
		memset(crypto_inst, 0, sizeof(*crypto_inst));
		crypto_inst->stype = serv_type;
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_CORE_AFFINITY_FORMAT,
			 i);
		if (adf_cfg_set_core_number_for_instance(accel_dev,
							 processed_sec,
							 key,
							 process_num,
							 &core_number))
			goto failed;

		if (strcmp(processed_sec, ADF_KERNEL_SEC) &&
		    strcmp(processed_sec, ADF_KERNEL_SAL_SEC))
			adf_cfg_add_key_value_param(accel_dev,
						    derived_sec,
						    key,
						    (void *)&core_number,
						    ADF_DEC);

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_NAME_FORMAT,
			 i);
		if (adf_cfg_get_param_value(accel_dev, processed_sec, key, val))
			goto failed;

		strlcpy(crypto_inst->name, val, sizeof(crypto_inst->name));

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_CY_POLL_MODE_FORMAT,
			 i);
		if (adf_cfg_set_value(
			accel_dev, processed_sec, key, &polling_mode))
			goto failed;

		crypto_inst->polling_mode = polling_mode;
		CPU_ZERO(&crypto_inst->affinity_mask);
		CPU_SET(core_number, &crypto_inst->affinity_mask);

		if (adf_cfg_get_ring_pairs(accel_dev->cfg->dev,
					   crypto_inst,
					   derived_sec,
					   accel_dev))
			goto failed;

		switch (serv_type) {
		case CRYPTO:
			adf_cfg_add_cy_inst_info(accel_dev,
						 crypto_inst,
						 derived_sec,
						 i);
			break;
		case ASYM:
			adf_cfg_add_asym_inst_info(accel_dev,
						   crypto_inst,
						   derived_sec,
						   i);
			break;
		case SYM:
			adf_cfg_add_sym_inst_info(accel_dev,
						  crypto_inst,
						  derived_sec,
						  i);
			break;
		default:
			pr_err("unknown crypto instance type %d.\n", serv_type);
			goto failed;
		}
	}

	ret = 0;
failed:
	free(crypto_inst, M_QAT);
	free(val, M_QAT);
	free(key, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to create rings for cy\n");

	return ret;
}

static int
adf_cfg_create_rings_entries_for_dc_inst(struct adf_accel_dev *accel_dev,
					 const char *processed_sec,
					 const char *derived_sec,
					 int process_num)
{
	int i = 0;
	int ret = EFAULT;
	unsigned long num_inst = 0, num_cy_inst = 0;
	unsigned long core_number = 0;
	unsigned long polling_mode = 0;
	struct adf_cfg_instance *dc_inst = NULL;

	char *key = NULL;
	char *val = NULL;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	ret = EFAULT;

	snprintf(key, ADF_CFG_MAX_STR_LEN, ADF_SERVICES_ENABLED);
	if (adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, key, val))
		goto failed;

	if (!strncmp(val, ADF_CFG_DC, ADF_CFG_MAX_VAL_LEN_IN_BYTES)) {
		strlcpy(key, ADF_NUM_CY, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
		if (adf_cfg_set_value(
			accel_dev, processed_sec, key, &num_cy_inst))
			goto failed;
		if (num_cy_inst > 0) {
			device_printf(
			    GET_DEV(accel_dev),
			    "NumCyInstances > 0,when DC only is enabled\n");
			goto failed;
		}
	}

	strlcpy(key, ADF_NUM_DC, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, processed_sec, key, &num_inst))
		goto failed;

	dc_inst = malloc(sizeof(*dc_inst), M_QAT, M_WAITOK | M_ZERO);

	for (i = 0; i < num_inst; i++) {
		memset(dc_inst, 0, sizeof(*dc_inst));
		dc_inst->stype = COMP;
		snprintf(key,
			 ADF_CFG_MAX_STR_LEN,
			 ADF_DC_CORE_AFFINITY_FORMAT,
			 i);

		if (adf_cfg_set_core_number_for_instance(accel_dev,
							 processed_sec,
							 key,
							 process_num,
							 &core_number))
			goto failed;

		if (strcmp(processed_sec, ADF_KERNEL_SEC) &&
		    strcmp(processed_sec, ADF_KERNEL_SAL_SEC)) {
			adf_cfg_add_key_value_param(accel_dev,
						    derived_sec,
						    key,
						    (void *)&core_number,
						    ADF_DEC);
		}

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_DC_NAME_FORMAT,
			 i);
		if (adf_cfg_get_param_value(accel_dev, processed_sec, key, val))
			goto failed;

		strlcpy(dc_inst->name, val, sizeof(dc_inst->name));

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_DC_POLL_MODE_FORMAT,
			 i);
		if (adf_cfg_set_value(
			accel_dev, processed_sec, key, &polling_mode))
			goto failed;

		dc_inst->polling_mode = polling_mode;
		CPU_ZERO(&dc_inst->affinity_mask);
		CPU_SET(core_number, &dc_inst->affinity_mask);

		if (adf_cfg_get_ring_pairs(
			accel_dev->cfg->dev, dc_inst, derived_sec, accel_dev))
			goto failed;

		adf_cfg_add_dc_inst_info(accel_dev, dc_inst, derived_sec, i);
	}

	ret = 0;
failed:
	free(dc_inst, M_QAT);
	free(val, M_QAT);
	free(key, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to create rings for dc\n");

	return ret;
}

static int
adf_cfg_process_user_section(struct adf_accel_dev *accel_dev,
			     const char *sec_name,
			     int dev)
{
	int i = 0;
	int ret = EFAULT;
	unsigned long num_processes = 0;
	unsigned long limit_dev_acc = 0;
	u8 serv_type = 0;

	char *key = NULL;
	char *val = NULL;
	char *derived_sec_name = NULL;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	derived_sec_name =
	    malloc(ADF_CFG_MAX_STR_LEN, M_QAT, M_WAITOK | M_ZERO);

	strlcpy(key, ADF_NUM_PROCESSES, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, sec_name, key, &num_processes))
		num_processes = 0;

	strlcpy(key, ADF_LIMIT_DEV_ACCESS, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, sec_name, key, &limit_dev_acc))
		limit_dev_acc = 0;

	for (i = 0; i < num_processes; i++) {
		if (limit_dev_acc)
			snprintf(derived_sec_name,
				 ADF_CFG_MAX_STR_LEN,
				 ADF_LIMITED_USER_SECTION_NAME_FORMAT,
				 sec_name,
				 dev,
				 i);
		else
			snprintf(derived_sec_name,
				 ADF_CFG_MAX_STR_LEN,
				 ADF_USER_SECTION_NAME_FORMAT,
				 sec_name,
				 i);

		if (adf_cfg_derived_section_add(accel_dev, derived_sec_name))
			goto failed;

		/* copy items to the derived section */
		adf_cfg_section_copy(accel_dev, sec_name, derived_sec_name);

		for (serv_type = NA; serv_type <= USED; serv_type++) {
			switch (serv_type) {
			case NA:
				break;
			case CRYPTO:
			case ASYM:
			case SYM:
				if (adf_cfg_is_svc_enabled(accel_dev,
							   serv_type))
					if (adf_cfg_create_rings_entries_for_cy_inst(
						accel_dev,
						sec_name,
						derived_sec_name,
						i,
						(enum adf_cfg_service_type)
						    serv_type))
						goto failed;
				break;
			case COMP:
				if (adf_cfg_is_svc_enabled(accel_dev,
							   serv_type))
					if (adf_cfg_create_rings_entries_for_dc_inst(
						accel_dev,
						sec_name,
						derived_sec_name,
						i))
						goto failed;
				break;
			case USED:
				break;
			default:
				pr_err("Unknown service type %d.\n", serv_type);
			}
		}
	}

	ret = 0;
failed:

	free(val, M_QAT);
	free(key, M_QAT);
	free(derived_sec_name, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to process user section %s\n",
			      sec_name);

	return ret;
}

static int
adf_cfg_cleanup_user_section(struct adf_accel_dev *accel_dev,
			     const char *sec_name)
{
	struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, sec_name);
	struct list_head *head;
	struct list_head *list_ptr, *tmp;

	if (!sec)
		return EFAULT;

	if (sec->is_derived)
		return 0;

	head = &sec->param_head;
	list_for_each_prev_safe(list_ptr, tmp, head)
	{
		struct adf_cfg_key_val *ptr =
		    list_entry(list_ptr, struct adf_cfg_key_val, list);

		if (!strcmp(ptr->key, ADF_LIMIT_DEV_ACCESS))
			continue;

		list_del(list_ptr);
		free(ptr, M_QAT);
	}
	return 0;
}

static int
adf_cfg_process_section_no_op(struct adf_accel_dev *accel_dev,
			      const char *sec_name)
{
	return 0;
}

static int
adf_cfg_cleanup_general_section(struct adf_accel_dev *accel_dev,
				const char *sec_name)
{
	unsigned long first_used_bundle = 0;
	int ret = EFAULT;
	char *key = NULL;
	char *val = NULL;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	/* Remove sections that not needed after processing */
	strlcpy(key, ADF_CONFIG_VERSION, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_remove_key_param(accel_dev, sec_name, key))
		goto failed;

	strlcpy(key, ADF_CY ADF_RING_ASYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_remove_key_param(accel_dev, sec_name, key))
		goto failed;

	strlcpy(key, ADF_CY ADF_RING_SYM_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_remove_key_param(accel_dev, sec_name, key))
		goto failed;

	strlcpy(key, ADF_DC ADF_RING_DC_SIZE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_remove_key_param(accel_dev, sec_name, key))
		goto failed;

	/* After all processing done, set the "FirstUserBundle" value */
	first_used_bundle = accel_dev->cfg->dev->max_kernel_bundle_nr + 1;
	strlcpy(key, ADF_FIRST_USER_BUNDLE, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_add_key_value_param(
		accel_dev, sec_name, key, (void *)&first_used_bundle, ADF_DEC))
		goto failed;

	ret = 0;
failed:
	free(key, M_QAT);
	free(val, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to clean up general section\n");

	return ret;
}

static int
adf_cfg_process_kernel_section(struct adf_accel_dev *accel_dev,
			       const char *sec_name)
{
	u8 serv_type = 0;

	for (serv_type = NA; serv_type <= USED; serv_type++) {
		switch (serv_type) {
		case NA:
			break;
		case CRYPTO:
		case ASYM:
		case SYM:
			if (adf_cfg_is_svc_enabled(accel_dev, serv_type))
				if (adf_cfg_create_rings_entries_for_cy_inst(
					accel_dev,
					sec_name,
					sec_name,
					0,
					(enum adf_cfg_service_type)serv_type))
					goto failed;
			break;
		case COMP:
			if (adf_cfg_is_svc_enabled(accel_dev, serv_type))
				if (adf_cfg_create_rings_entries_for_dc_inst(
					accel_dev, sec_name, sec_name, 0))
					goto failed;
			break;
		case USED:
			break;
		default:
			pr_err("Unknown service type of instance %d.\n",
			       serv_type);
		}
	}

	return 0;

failed:
	return EFAULT;
}

static int
adf_cfg_cleanup_kernel_section(struct adf_accel_dev *accel_dev,
			       const char *sec_name)
{
	return 0;
}

static int
adf_cfg_create_accel_section(struct adf_accel_dev *accel_dev,
			     const char *sec_name)
{
	/* Find global settings for coalescing. Use defaults if not found */
	unsigned long accel_coales = 0;
	unsigned long accel_coales_timer = 0;
	unsigned long accel_coales_num_msg = 0;
	unsigned long cpu;
	char *key = NULL;
	char *val = NULL;
	int ret = EFAULT;
	int index = 0;
	struct adf_hw_device_data *hw_device = accel_dev->hw_device;

	if (!hw_device)
		goto failed;

	key = malloc(ADF_CFG_MAX_KEY_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	val = malloc(ADF_CFG_MAX_VAL_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	strlcpy(key,
		ADF_ETRMGR_COALESCING_ENABLED,
		ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(accel_dev, ADF_GENERAL_SEC, key, &accel_coales))
		accel_coales = ADF_CFG_ACCEL_DEF_COALES;

	strlcpy(key, ADF_ETRMGR_COALESCE_TIMER, ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(
		accel_dev, ADF_GENERAL_SEC, key, &accel_coales_timer))
		accel_coales_timer = ADF_CFG_ACCEL_DEF_COALES_TIMER;

	strlcpy(key,
		ADF_ETRMGR_COALESCING_MSG_ENABLED,
		ADF_CFG_MAX_KEY_LEN_IN_BYTES);
	if (adf_cfg_set_value(
		accel_dev, ADF_GENERAL_SEC, key, &accel_coales_num_msg))
		accel_coales_num_msg = ADF_CFG_ACCEL_DEF_COALES_NUM_MSG;

	for (index = 0; index < hw_device->num_banks; index++) {
		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_ETRMGR_COALESCING_ENABLED_FORMAT,
			 index);
		ret = adf_cfg_add_key_value_param(
		    accel_dev, sec_name, key, &accel_coales, ADF_DEC);
		if (ret != 0)
			goto failed;

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_ETRMGR_COALESCE_TIMER_FORMAT,
			 index);
		ret = adf_cfg_add_key_value_param(
		    accel_dev, sec_name, key, &accel_coales_timer, ADF_DEC);
		if (ret != 0)
			goto failed;

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_ETRMGR_COALESCING_MSG_ENABLED_FORMAT,
			 index);
		ret = adf_cfg_add_key_value_param(
		    accel_dev, sec_name, key, &accel_coales_num_msg, ADF_DEC);
		if (ret != 0)
			goto failed;

		cpu = ADF_CFG_AFFINITY_WHATEVER;

		snprintf(key,
			 ADF_CFG_MAX_KEY_LEN_IN_BYTES,
			 ADF_ETRMGR_CORE_AFFINITY_FORMAT,
			 index);
		ret = adf_cfg_add_key_value_param(
		    accel_dev, sec_name, key, &cpu, ADF_DEC);
		if (ret != 0)
			goto failed;
	}

	ret = 0;

failed:
	free(key, M_QAT);
	free(val, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to create accel section\n");

	return ret;
}

static int
adf_cfg_cleanup_accel_section(struct adf_accel_dev *accel_dev,
			      const char *sec_name)
{
	return 0;
}

static int
adf_cfg_process_accel_section(struct adf_accel_dev *accel_dev,
			      const char *sec_name)
{
	int accel_num = 0;
	struct adf_hw_device_data *hw_device = accel_dev->hw_device;
	char *derived_name = NULL;
	int ret = EFAULT;

	if (!hw_device)
		goto failed;

	if (hw_device->num_logical_accel == 0)
		goto failed;

	derived_name =
	    malloc(ADF_CFG_MAX_SECTION_LEN_IN_BYTES, M_QAT, M_WAITOK | M_ZERO);

	for (accel_num = 0; accel_num < hw_device->num_logical_accel;
	     accel_num++) {
		snprintf(derived_name,
			 ADF_CFG_MAX_SECTION_LEN_IN_BYTES,
			 ADF_ACCEL_STR,
			 accel_num);
		ret = adf_cfg_section_add(accel_dev, derived_name);
		if (ret != 0)
			goto failed;

		ret = adf_cfg_create_accel_section(accel_dev, derived_name);
		if (ret != 0)
			goto failed;
	}

	ret = 0;
failed:
	free(derived_name, M_QAT);

	if (ret)
		device_printf(GET_DEV(accel_dev),
			      "Failed to process accel section\n");

	return ret;
}

int
adf_cfg_process_section(struct adf_accel_dev *accel_dev,
			const char *sec_name,
			int dev)
{
	if (!strcmp(sec_name, ADF_GENERAL_SEC) ||
	    !strcmp(sec_name, ADF_INLINE_SEC))
		return adf_cfg_process_section_no_op(accel_dev, sec_name);
	else if (!strcmp(sec_name, ADF_KERNEL_SEC) ||
		 !strcmp(sec_name, ADF_KERNEL_SAL_SEC))
		return adf_cfg_process_kernel_section(accel_dev, sec_name);
	else if (!strcmp(sec_name, ADF_ACCEL_SEC))
		return adf_cfg_process_accel_section(accel_dev, sec_name);
	else
		return adf_cfg_process_user_section(accel_dev, sec_name, dev);
}

int
adf_cfg_cleanup_section(struct adf_accel_dev *accel_dev,
			const char *sec_name,
			int dev)
{
	if (!strcmp(sec_name, ADF_GENERAL_SEC))
		return adf_cfg_cleanup_general_section(accel_dev, sec_name);
	else if (!strcmp(sec_name, ADF_INLINE_SEC))
		return adf_cfg_process_section_no_op(accel_dev, sec_name);
	else if (!strcmp(sec_name, ADF_KERNEL_SEC) ||
		 !strcmp(sec_name, ADF_KERNEL_SAL_SEC))
		return adf_cfg_cleanup_kernel_section(accel_dev, sec_name);
	else if (strstr(sec_name, ADF_ACCEL_SEC))
		return adf_cfg_cleanup_accel_section(accel_dev, sec_name);
	else
		return adf_cfg_cleanup_user_section(accel_dev, sec_name);
}

int
adf_cfg_setup_irq(struct adf_accel_dev *accel_dev)
{
	int ret = EFAULT;
	struct adf_accel_pci *info_pci_dev = &accel_dev->accel_pci_dev;
	struct adf_cfg_device *cfg_dev = NULL;
	struct msix_entry *msixe = NULL;
	u32 num_msix = 0;
	int index = 0;
	int computed_core = 0;

	if (!accel_dev || !accel_dev->cfg || !accel_dev->hw_device)
		goto failed;

	cfg_dev = accel_dev->cfg->dev;
	if (!cfg_dev)
		goto failed;

	msixe =
	    (struct msix_entry *)accel_dev->accel_pci_dev.msix_entries.entries;
	num_msix = accel_dev->accel_pci_dev.msix_entries.num_entries;
	if (!msixe)
		goto cleanup_and_fail;

	/*
	 * Here we want to set the affinity of kernel and epoll mode
	 * bundle into user defined value.
	 * Because in adf_isr.c we setup core affinity by round-robin
	 * we need to reset it after device up done.
	 */
	for (index = 0; index < accel_dev->hw_device->num_banks; index++) {
		struct adf_cfg_bundle *bundle = cfg_dev->bundles[index];

		if (!bundle)
			continue;

		if (bundle->type != KERNEL &&
		    bundle->polling_mode != ADF_CFG_RESP_EPOLL)
			continue;

		if (bundle->number >= num_msix)
			goto cleanup_and_fail;

		computed_core = CPU_FFS(&bundle->affinity_mask) - 1;
		bus_bind_intr(info_pci_dev->pci_dev,
			      msixe[index].irq,
			      computed_core);
	}
	ret = 0;

cleanup_and_fail:
	adf_cfg_device_clear(cfg_dev, accel_dev);
	free(cfg_dev, M_QAT);
	accel_dev->cfg->dev = NULL;

failed:
	return ret;
}
