/*
 * Copyright (c) 2025, MediaTek Inc. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <common/debug.h>

/* MTK header */
#include <drivers/pmic/pmic_swap_api.h>
#include <lpm_v2/mt_lp_api.h>
#include <lpm_v2/mt_lp_rq.h>
#include <mtk_bl31_interface.h>
#include <mtk_sip_svc.h>

/* UFS generic control flags */
#define UFS_MTK_SIP_VA09_PWR_CTRL		BIT(0)
#define UFS_MTK_SIP_DEVICE_RESET		BIT(1)
#define UFS_MTK_SIP_CRYPTO_CTRL			BIT(2)
#define UFS_MTK_SIP_REF_CLK_NOTIFICATION	BIT(3)
#define UFS_MTK_SIP_SRAM_PWR_CTRL		BIT(5)
#define UFS_MTK_SIP_GET_VCC_INFO		BIT(6)
#define UFS_MTK_SIP_DEVICE_PWR_CTRL		BIT(7)
#define UFS_MTK_SIP_MPHY_CTRL			BIT(8)
#define UFS_MTK_SIP_MTCMOS_CTRL			BIT(9)

enum {
	VCC_NONE = 0,
	VCC_1,
	VCC_2,
};

static void ufs_get_vcc_info(struct smccc_res *smccc_ret)
{
	if (smccc_ret == NULL)
		return;

	if (is_second_pmic_pp_swap())
		smccc_ret->a1 = VCC_2;
	else
		smccc_ret->a1 = VCC_1;
}

/* SPM resource control */
#define RSC_MEM			(MT_LP_RQ_DRAM | MT_LP_RQ_EMI)
#define RSC_PMIC		MT_LP_RQ_PMIC

static int ufs_rsc_ctrl(unsigned int rsc, bool hold)
{
	static struct mt_lp_resource_user ufs_res_user;
	int ret = -1;

	if (!ufs_res_user.uid) {
		ret = mt_lp_resource_user_register("UFS", &ufs_res_user);

		if (ret) {
			WARN("%s: register lp resource failed\n", __func__);
			return ret;
		}
	}

	if (hold)
		ret = ufs_res_user.request(&ufs_res_user, rsc);
	else
		ret = ufs_res_user.release(&ufs_res_user);

	VERBOSE("%s: rsc=%d, hold=%d\n", __func__, rsc, hold);

	if (ret)
		WARN("%s: RSC_%d %s failed\n", __func__, rsc, hold ? "request" : "release");

	return ret;
}

int ufs_rsc_ctrl_mem(bool hold)
{
	return ufs_rsc_ctrl(RSC_MEM, hold);
}

int ufs_rcs_ctrl_pmic(bool hold)
{
	return ufs_rsc_ctrl(RSC_PMIC, hold);
}

/* UFS clock status */
static uint32_t ufs_clk_sta = UFS_REF_CLK_ON;

bool ufs_is_clk_status_off(void)
{
	return ufs_clk_sta == UFS_REF_CLK_OFF;
}

void ufs_set_clk_status(bool on)
{
	if (on)
		ufs_clk_sta = UFS_REF_CLK_ON;
	else
		ufs_clk_sta = UFS_REF_CLK_OFF;
}

static u_register_t ufs_knl_ctrl(u_register_t x1,
				 u_register_t x2,
				 u_register_t x3,
				 u_register_t x4,
				 void *handle,
				 struct smccc_res *smccc_ret)
{
	uint64_t ret = 0;

	switch (x1) {
	case UFS_MTK_SIP_VA09_PWR_CTRL:
		ufs_mphy_va09_cg_ctrl(!!x2);
		break;
	case UFS_MTK_SIP_DEVICE_RESET:
		ufs_device_reset_ctrl(!!x2);
		break;
	case UFS_MTK_SIP_CRYPTO_CTRL:
		ufs_crypto_hie_init();
		break;
	case UFS_MTK_SIP_REF_CLK_NOTIFICATION:
		ufs_ref_clk_status(x2, x3);
		break;
	case UFS_MTK_SIP_SRAM_PWR_CTRL:
		ufs_sram_pwr_ctrl(x2);
		break;
	case UFS_MTK_SIP_GET_VCC_INFO:
		ufs_get_vcc_info(smccc_ret);
		break;
	case UFS_MTK_SIP_DEVICE_PWR_CTRL:
		ufs_device_pwr_ctrl(x2, x3);
		break;
	case UFS_MTK_SIP_MPHY_CTRL:
		ufs_mphy_ctrl(x2);
		break;
	case UFS_MTK_SIP_MTCMOS_CTRL:
#if defined(CONFIG_MTK_MTCMOS)
		ufs_mtcmos_ctrl(!!x2);
#endif
		break;
	default:
		ret = -1;
		WARN("[UFS] invalid argument 0x%lx from kernel\n", x1);
		break;
	}

	return ret;
}

static u_register_t ufs_bl_ctrl(u_register_t x1,
				u_register_t x2,
				u_register_t x3,
				u_register_t x4,
				void *handle,
				struct smccc_res *smccc_ret)
{
	uint64_t ret = 0;

	switch (x1) {
	case UFS_MTK_SIP_DEVICE_RESET:
		ufs_device_reset_ctrl(x2);
		break;
	default:
		ret = -1;
		WARN("[UFS] invalid argument 0x%lx from bootloader\n", x1);
		break;
	}

	return ret;
}

DECLARE_SMC_HANDLER(MTK_SIP_KERNEL_UFS_CONTROL, ufs_knl_ctrl);
DECLARE_SMC_HANDLER(MTK_SIP_BL_UFS_CONTROL, ufs_bl_ctrl);
