// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2019 MediaTek Inc.
 */

#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>

#if defined(CONFIG_MTK_TINYSYS_SSPM_V2)
#include <sspm_ipi_id.h>
#include <sspm_define.h>
#endif

#include "mtk_qos_common.h"
#include "mtk_qos_bound.h"
#include "mtk_qos_ipi.h"

#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_V2)
static DEFINE_MUTEX(qos_ipi_mutex);
static int qos_sspm_ready;
int qos_ipi_ackdata;
struct qos_ipi_data qos_recv_ackdata;

static int qos_ipi_recv_thread(void *arg)
{
	struct qos_ipi_data *qos_ipi_d;
	int bound_cmd_id = qos_get_ipi_cmd(QOS_IPI_QOS_BOUND);

	pr_info("%s start!\n", __func__);
	do {
		mtk_ipi_recv(&sspm_ipidev, IPIR_I_QOS);

		qos_ipi_d = &qos_recv_ackdata;

		if (qos_ipi_d->cmd == bound_cmd_id)
			qos_notifier_call_chain(
					qos_ipi_d->u.qos_bound.state,
					get_qos_bound());
		else
			pr_info("wrong QoS IPI command: %d\n", qos_ipi_d->cmd);

	} while (!kthread_should_stop());

	return 0;
}
#endif

int qos_ipi_to_sspm_command(void *buffer, int slot)
{
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_V2)
	int ret, ackdata;
	struct qos_ipi_data *qos_ipi_d = buffer;
	int slot_num = sizeof(struct qos_ipi_data)/SSPM_MBOX_SLOT_SIZE;

	mutex_lock(&qos_ipi_mutex);

	ret = qos_get_ipi_cmd(qos_ipi_d->cmd);
	/*
	if (qos_ipi_d->cmd < 0) {
		pr_info("qos ipi cmd get error (in= %d, out = %d)\n",
			qos_ipi_d->cmd, ret);
		goto error;
	}
	*/
	qos_ipi_d->cmd = ret;

	if (qos_sspm_ready != 1) {
		pr_info("qos ipi not ready, skip cmd=%d\n", qos_ipi_d->cmd);
		goto error;
	}

	pr_info("qos ipi cmd(%d) send\n", qos_ipi_d->cmd);

	qos_ipi_ackdata = 0;

	if (slot > slot_num) {
		pr_info("qos ipi cmd %d req slot error(%d > %d)\n",
			qos_ipi_d->cmd, slot, slot_num);
		goto error;
	}

	ret = mtk_ipi_send_compl(&sspm_ipidev, IPIS_C_QOS,
		IPI_SEND_POLLING, buffer,
		slot_num, 2000);
	if (ret) {
		pr_info("qos ipi cmd %d send fail,ret=%d\n",
		qos_ipi_d->cmd, ret);
		goto error;
	}

	if (!qos_ipi_ackdata) {
		pr_info("qos ipi cmd %d ack fail, ackdata=%d\n",
		qos_ipi_d->cmd, qos_ipi_ackdata);
		goto error;
	}
	ackdata = qos_ipi_ackdata;
	mutex_unlock(&qos_ipi_mutex);
	return ackdata;
error:
	mutex_unlock(&qos_ipi_mutex);
#endif
	return -1;
}
EXPORT_SYMBOL_GPL(qos_ipi_to_sspm_command);

#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_V2)
static void qos_sspm_enable(void)
{
	struct qos_ipi_data qos_ipi_d;

	qos_ipi_d.cmd = QOS_IPI_QOS_ENABLE;
	qos_ipi_to_sspm_command(&qos_ipi_d, 1);
}
#endif

void qos_ipi_init(struct mtk_qos *qos)
{
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_V2)
	unsigned int ret;
	/* for AP to SSPM */
	ret = mtk_ipi_register(&sspm_ipidev, IPIS_C_QOS, NULL, NULL,
				(void *) &qos_ipi_ackdata);
	if (ret) {
		pr_info("qos IPIS_C_QOS ipi_register fail, ret %d\n", ret);
		qos_sspm_ready = -1;
		return;
	}

	/* for SSPM to AP */
	ret = mtk_ipi_register(&sspm_ipidev, IPIR_I_QOS, NULL, NULL,
				(void *) &qos_recv_ackdata);
	if (ret) {
		pr_info("qos IPIR_I_QOS ipi_register fail, ret %d\n", ret);
		qos_sspm_ready = -2;
		return;
	}
	qos_sspm_ready = 1;
	qos_sspm_enable();
	pr_info("%s ready!\n", __func__);
#endif
}
EXPORT_SYMBOL_GPL(qos_ipi_init);

void qos_ipi_recv_init(struct mtk_qos *qos)
{
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_V2)
	if (qos_sspm_ready != 1) {
		pr_info("QOS SSPM not ready, recv thread not start!\n");
		return;
	}
	kthread_run(qos_ipi_recv_thread, NULL, "qos_ipi_recv");
#endif
}
EXPORT_SYMBOL_GPL(qos_ipi_recv_init);
