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

#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk_dvfsrc.h>

#include "dvfsrc-met.h"
#include "dvfsrc-common.h"

static inline u32 dvfsrc_met_read(struct mtk_dvfsrc_met *dvfs, u32 offset)
{
	return readl(dvfs->regs + offset);
}

enum met_src_index {
	MD2SPM = 0,
	DDR__SW_REQ2,
	DDR__SW_REQ3,
	DDR__QOS_BW,
	DDR__EMI_TOTAL,
	DDR__HRT_BW,
	DDR__HIFI,
	DDR__HIFI_LATENCY,
	DDR__MD_LATENCY,
	DDR__MD_DDR,
	VCORE__SCP,
	VCORE__HIFI,
	VCORE__SRAMRC,
	VCORE__SW_REQ1,
	VCORE__SW_REQ2,
	VCORE__SW_REQ3,
	VCORE__SW_REQ4,
	VCORE__SW_REQ5,
	VCORE__SW_REQ6,
	VCORE__SW_REQ7,
	VCORE__SW_REQ8,
	SCP_REQ,
	PMQOS_TOTAL,
	PMQOS_BW0,
	PMQOS_BW1,
	PMQOS_BW2,
	PMQOS_BW3,
	PMQOS_BW4,
	PMQOS_BW5,
	PMQOS_BW6,
	PMQOS_BW7,
	PMQOS_BW8,
	PMQOS_BW9,
	HRT_MD_BW,
	HRT_DISP_BW,
	HRT_ISP_BW,
	MD_SCENARIO,
	HIFI_SCENARIO_IDX,
	MD_EMI_LATENCY,
	SRC_MAX,
};

/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
	"MD2SPM",
	"DDR__SW_REQ2",
	"DDR__SW_REQ3",
	"DDR__QOS_BW",
	"DDR__EMI_TOTAL",
	"DDR__HRT_BW",
	"DDR__HIFI",
	"DDR__HIFI_LATENCY",
	"DDR__MD_LATENCY",
	"DDR__MD_DDR",
	"VCORE__SCP",
	"VCORE__HIFI",
	"VCORE__SRAMRC",
	"VCORE__SW_REQ1",
	"VCORE__SW_REQ2",
	"VCORE__SW_REQ3",
	"VCORE__SW_REQ4",
	"VCORE__SW_REQ5",
	"VCORE__SW_REQ6",
	"VCORE__SW_REQ7",
	"VCORE__SW_REQ8",
	"SCP_REQ",
	"PMQOS_TOTAL",
	"PMQOS_BW0",
	"PMQOS_BW1",
	"PMQOS_BW2",
	"PMQOS_BW3",
	"PMQOS_BW4",
	"PMQOS_BW5",
	"PMQOS_BW6",
	"PMQOS_BW7",
	"PMQOS_BW8",
	"PMQOS_BW9",
	"HRT_MD_BW",
	"HRT_DISP_BW",
	"HRT_ISP_BW",
	"MD_SCENARIO",
	"HIFI_SCENARIO_IDX",
	"MD_EMI_LATENCY",
};

#define DVFSRC_SW_REQ1	(0x10)
#define DVFSRC_SW_REQ2	(0x14)
#define DVFSRC_SW_REQ3	(0x18)
#define DVFSRC_SW_REQ4	(0x1C)
#define DVFSRC_SW_REQ5	(0x20)
#define DVFSRC_SW_REQ6	(0x24)
#define DVFSRC_SW_REQ7	(0x28)
#define DVFSRC_SW_REQ8	(0x2C)

#define DVFSRC_ISP_HRT  (0x20C)

#define DVFSRC_VCORE_REQUEST (0x80)
#define DVFSRC_DEBUG_STA_0 (0x29C)
#define DVFSRC_DEBUG_STA_2 (0x2A4)
#define DVFSRC_DEBUG_STA_8 (0x2BC)

#define DVFSRC_PCIE_VCORE_REQ (0xE4)

#define DVFSRC_SW_BW_0	(0x1DC)
#define DVFSRC_SW_BW_1	(0x1E0)
#define DVFSRC_SW_BW_2	(0x1E4)
#define DVFSRC_SW_BW_3	(0x1E8)
#define DVFSRC_SW_BW_4	(0x1EC)
#define DVFSRC_SW_BW_5	(0x1F0)
#define DVFSRC_SW_BW_6	(0x1F4)
#define DVFSRC_SW_BW_7	(0x1F8)
#define DVFSRC_SW_BW_8	(0x1FC)
#define DVFSRC_SW_BW_9	(0x200)

#define DVFSRC_DDR_REQUEST	(0x2C8)
#define DVFSRC_DDR_REQUEST3	(0x2D0)

#define DVFSRC_MD_DDR_FLOOR_REQUEST (0x5E4)
#define DVFSRC_QOS_DDR_REQUEST (0x5E8)

#define DVFSRC_LEVEL_HEX  (0x5F0)

/* DVFSRC_SW_REQX */
#define DDR_SW_AP_SHIFT		12
#define DDR_SW_AP_MASK		0xF
#define VCORE_SW_AP_SHIFT	4
#define VCORE_SW_AP_MASK	0x7

/* DVFSRC_VCORE_REQUEST 0x70 */
#define VCORE_SCP_GEAR_SHIFT	12
#define VCORE_SCP_GEAR_MASK	0x7

/* DVFSRC_SRARMC */
#define VCORE_SRAMRC_GEAR_SHIFT	18
#define VCORE_SRAMRC_GEAR_MASK	0x7

/* met profile function */
static int dvfsrc_get_src_req_num(void)
{
	return SRC_MAX;
}

static char **dvfsrc_get_src_req_name(void)
{
	return met_src_name;
}

static u32 dvfsrc_get_current_level(struct mtk_dvfsrc_met *dvfsrc)
{
	return dvfsrc_met_read(dvfsrc, DVFSRC_LEVEL_HEX) & 0x3F;
}

static u32 dvfsrc_ddr_qos(struct mtk_dvfsrc_met *dvfsrc)
{
	return dvfsrc_met_read(dvfsrc, DVFSRC_QOS_DDR_REQUEST);
}

static int dvfsrc_emi_mon_gear(struct mtk_dvfsrc_met *dvfs)
{
	unsigned int total_bw_status;
	int i;
	int idx = -1;
	u32 gear;
	u32 max_idx = dvfs->dvd->max_emi_mon;

	if (max_idx >= MAX_EMI_MON_COUNT)
		return 0;

	total_bw_status = dvfsrc_met_read(dvfs, DVFSRC_DEBUG_STA_2);
	for (i = max_idx; i >= 0 ; i--) {
		if (total_bw_status & GENMASK(i, i)) {
			idx = i;
			break;
		}
	}

	if ((idx >= 0) && (idx < 4)) {
		gear = dvfsrc_met_read(dvfs, DVFSRC_DDR_REQUEST);
		return ((gear >> (idx * 4)) & 0xF);
	} else if ((idx >= 4) && (idx <= max_idx)) {
		idx = idx - 4;
		gear = dvfsrc_met_read(dvfs, DVFSRC_DDR_REQUEST3);
		return ((gear >> (idx * 4)) & 0xF);
	} else
		return 0;
}

static void vcorefs_get_src_ddr_req(struct mtk_dvfsrc_met *dvfs)
{
	unsigned int sw_req;

	sw_req = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ2);
	met_vcorefs_src[DDR__SW_REQ2] = (sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;

	sw_req = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ3);
	met_vcorefs_src[DDR__SW_REQ3] = (sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;

	met_vcorefs_src[DDR__QOS_BW] = dvfsrc_ddr_qos(dvfs);

	met_vcorefs_src[DDR__EMI_TOTAL] = dvfsrc_emi_mon_gear(dvfs);

	met_vcorefs_src[DDR__HRT_BW] = mtk_dvfsrc_query_debug_info(DVFSRC_HRT_BW_DDR_REQ);

	met_vcorefs_src[DDR__HIFI] = mtk_dvfsrc_query_debug_info(DVFSRC_HIFI_DDR_REQ);

	met_vcorefs_src[DDR__HIFI_LATENCY] =
		mtk_dvfsrc_query_debug_info(DVFSRC_HIFI_RISING_DDR_REQ);

	met_vcorefs_src[DDR__MD_LATENCY] = mtk_dvfsrc_query_debug_info(DVFSRC_MD_IMP_DDR_REQ);

	met_vcorefs_src[DDR__MD_DDR] = mtk_dvfsrc_query_debug_info(DVFSRC_MD_SCEN_DDR_REQ);
}

static void vcorefs_get_src_vcore_req(struct mtk_dvfsrc_met *dvfs)
{
	u32 val;
	u32 sta;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ1);
	met_vcorefs_src[VCORE__SW_REQ1] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ2);
	met_vcorefs_src[VCORE__SW_REQ2] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ3);
	met_vcorefs_src[VCORE__SW_REQ3] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ4);
	met_vcorefs_src[VCORE__SW_REQ4] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ5);
	met_vcorefs_src[VCORE__SW_REQ5] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ6);
	met_vcorefs_src[VCORE__SW_REQ6] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ7);
	met_vcorefs_src[VCORE__SW_REQ7] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	val = dvfsrc_met_read(dvfs, DVFSRC_SW_REQ8);
	met_vcorefs_src[VCORE__SW_REQ8] = (val >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;

	met_vcorefs_src[VCORE__HIFI] = mtk_dvfsrc_query_debug_info(DVFSRC_HIFI_VCORE_REQ);

	sta = dvfsrc_met_read(dvfs, DVFSRC_DEBUG_STA_2);
	if ((sta >> 14) & 0x1) {
		val = dvfsrc_met_read(dvfs, DVFSRC_VCORE_REQUEST);
		met_vcorefs_src[VCORE__SCP] = (val >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
	} else
		met_vcorefs_src[VCORE__SCP] = 0;

	sta = dvfsrc_met_read(dvfs, DVFSRC_DEBUG_STA_8);
	met_vcorefs_src[VCORE__SRAMRC] = (sta >> VCORE_SRAMRC_GEAR_SHIFT) & VCORE_SRAMRC_GEAR_MASK;
}

static void vcorefs_get_src_misc_info(struct mtk_dvfsrc_met *dvfs)
{
	u32 i;
	u32 sta0, sta2;

	met_vcorefs_src[PMQOS_BW0] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_0);
	met_vcorefs_src[PMQOS_BW1] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_1);
	met_vcorefs_src[PMQOS_BW2] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_2);
	met_vcorefs_src[PMQOS_BW3] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_3);
	met_vcorefs_src[PMQOS_BW4] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_4);
	met_vcorefs_src[PMQOS_BW5] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_5);
	met_vcorefs_src[PMQOS_BW6] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_6);
	met_vcorefs_src[PMQOS_BW7] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_7);
	met_vcorefs_src[PMQOS_BW8] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_8);
	met_vcorefs_src[PMQOS_BW9] = dvfsrc_met_read(dvfs, DVFSRC_SW_BW_9);
	met_vcorefs_src[PMQOS_TOTAL] = 0;

	for (i = 0; i < 9; i++) {
		if (i == 6)
			continue;
		met_vcorefs_src[PMQOS_TOTAL] += met_vcorefs_src[PMQOS_BW0 + i];
	}
	met_vcorefs_src[HRT_ISP_BW] = dvfsrc_met_read(dvfs, DVFSRC_ISP_HRT);
	met_vcorefs_src[HRT_MD_BW] = mtk_dvfsrc_query_debug_info(DVFSRC_MD_HRT_BW);

	sta0 = dvfsrc_met_read(dvfs, DVFSRC_DEBUG_STA_0);
	met_vcorefs_src[MD2SPM] = sta0 & 0xFFFF;
	met_vcorefs_src[MD_SCENARIO] = sta0 & 0xFFFF;

	sta2 = dvfsrc_met_read(dvfs, DVFSRC_DEBUG_STA_2);
	met_vcorefs_src[SCP_REQ] = (sta2 >> 14) & 0x1;
	met_vcorefs_src[MD_EMI_LATENCY] = (sta2 >> 12) & 0x3;
	met_vcorefs_src[HIFI_SCENARIO_IDX] = (sta2 >> 16) & 0xFF;
}


static unsigned int *dvfsrc_get_src_req(struct mtk_dvfsrc_met *dvfs)
{
	vcorefs_get_src_ddr_req(dvfs);
	vcorefs_get_src_vcore_req(dvfs);
	vcorefs_get_src_misc_info(dvfs);

	return met_vcorefs_src;
}

static int dvfsrc_get_ddr_ratio(struct mtk_dvfsrc_met *dvfs)
{
	int dram_opp;

	dram_opp = mtk_dvfsrc_query_opp_info(MTK_DVFSRC_CURR_DRAM_OPP);

	return 0;
}

const struct dvfsrc_met_config mt6983_met_config = {
	.dvfsrc_get_src_req_num = dvfsrc_get_src_req_num,
	.dvfsrc_get_src_req_name = dvfsrc_get_src_req_name,
	.dvfsrc_get_src_req = dvfsrc_get_src_req,
	.dvfsrc_get_ddr_ratio = dvfsrc_get_ddr_ratio,
	.get_current_level = dvfsrc_get_current_level,
};
