// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2018, 2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 */
#include "hab.h"
#include <linux/of.h>

/*
 * set valid mmid value in tbl to show this is valid entry. All inputs here are
 * normalized to 1 based integer
 */
static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
				   int32_t vm_range, int32_t mmid_start,
				   int32_t mmid_range, int32_t be, int kernel_only)
{
	int i, j;

	for (i = vm_start; i < vm_start+vm_range; i++) {
		tbl[i].vmid = i; /* set valid vmid value to make it usable */
		for (j = mmid_start; j < mmid_start + mmid_range; j++) {
			/* sanity check */
			if (tbl[i].mmid[j] != HABCFG_VMID_INVALID) {
				pr_err("overwrite previous setting vmid %d, mmid %d, be %d, kernel only %d\n",
					i, j, tbl[i].is_listener[j], tbl[i].kernel_only[j]);
			}
			tbl[i].mmid[j] = j;
			tbl[i].is_listener[j] = be; /* BE IS listen */
			tbl[i].kernel_only[j] = kernel_only;
		}
	}

	return 0;
}

void dump_settings(struct local_vmid *settings)
{
	pr_debug("self vmid is %d\n", settings->self);
}


#ifdef CONFIG_MSM_VHOST_HAB
int fill_default_gvm_settings(struct local_vmid *settings, int vmid_default,
		int mmid_start, int mmid_end)
{
	int32_t be = HABCFG_BE_TRUE;
	int32_t range = 1;
	int32_t vmremote = vmid_default;
	/* pchan is not kernel only by default */
	int32_t kernel_only = 0;

	/* default gvm always talks to host as vm0 */
	settings->self = 0;
	return fill_vmid_mmid_tbl(settings->vmid_mmid_list, vmremote, range,
		mmid_start/100, (mmid_end-mmid_start)/100+1, be, kernel_only);
}
#else
int fill_default_gvm_settings(struct local_vmid *settings, int vmid_local,
		int mmid_start, int mmid_end)
{
	int32_t be = HABCFG_BE_FALSE;
	int32_t range = 1;
	int32_t vmremote = 0; /* default to host[0] as local is guest[2] */
	/* pchan is not kernel only by default */
	int32_t kernel_only = 0;

	settings->self = vmid_local;
	/* default gvm always talks to host as vm0 */
	return fill_vmid_mmid_tbl(settings->vmid_mmid_list, vmremote, range,
		mmid_start/100, (mmid_end-mmid_start)/100+1, be, kernel_only);
}
#endif

/* device tree based parser */
static int hab_parse_dt(struct local_vmid *settings)
{
	int result, i;
	struct device_node *hab_node = NULL;
	struct device_node *mmid_grp_node = NULL;
	const char *role = NULL;
	int tmp = -1, vmids_num;
	u32 vmids[16];
	int32_t grp_start_id, be;
	int kernel_only;

	/* parse device tree*/
	pr_debug("parsing hab node in device tree...\n");
	hab_node = of_find_compatible_node(NULL, NULL, "qcom,hab");
	if (!hab_node) {
		pr_err("no hab device tree node\n");
		return -ENODEV;
	}

	/* read the local vmid of this VM, like 0 for host, 1 for AGL GVM */
	result = of_property_read_u32(hab_node, "vmid", &tmp);
	if (result) {
		pr_err("failed to read local vmid, result = %d\n", result);
		return result;
	}

	pr_debug("local vmid = %d\n", tmp);
	settings->self = tmp;

	for_each_child_of_node(hab_node, mmid_grp_node) {
		/* read the group starting id */
		result = of_property_read_u32(mmid_grp_node,
				"grp-start-id", &tmp);
		if (result) {
			pr_err("failed to read grp-start-id, result = %d\n",
				result);
			return result;
		}

		pr_debug("grp-start-id = %d\n", tmp);
		grp_start_id = tmp;

		/* read the role(fe/be) of these pchans in this mmid group */
		result = of_property_read_string(mmid_grp_node, "role", &role);
		if (result) {
			pr_err("failed to get role, result = %d\n", result);
			return result;
		}

		pr_debug("local role of this mmid group is %s\n", role);
		if (!strcmp(role, "be"))
			be = 1;
		else
			be = 0;

		/* read the remote vmids for these pchans in this mmid group */
		vmids_num = of_property_count_elems_of_size(mmid_grp_node,
					"remote-vmids", sizeof(u32));

		result = of_property_read_u32_array(mmid_grp_node,
					"remote-vmids", vmids, vmids_num);
		if (result) {
			pr_err("failed to read remote-vmids, result = %d\n",
				result);
			return result;
		}

		/* check the kernel_only flag for these pchans in this mmid group */
		result = of_property_read_bool(mmid_grp_node, "kernel_only");
		if (result) {
			kernel_only = 1;
			pr_debug("kernel_only flag is set for this mmid group\n");
		} else {
			kernel_only = 0;
			pr_debug("kernel_only flag is not set for this mmid group\n");
		}

		for (i = 0; i < vmids_num; i++) {
			pr_debug("vmids_num = %d, vmids[%d] = %d\n",
				vmids_num, i, vmids[i]);

			result = fill_vmid_mmid_tbl(
					settings->vmid_mmid_list,
					vmids[i], 1,
					grp_start_id/100, 1, be, kernel_only);
			if (result) {
				pr_err("fill_vmid_mmid_tbl failed\n");
				return result;
			}
		}

	}

	dump_settings(settings);
	return 0;
}

/*
 * 0: successful
 * negative: various failure core
 */
int hab_parse(struct local_vmid *settings)
{
	int ret;

	ret = hab_parse_dt(settings);

	return ret;
}
