/* SPDX-License-Identifier: GPL-2.0-only */


#ifndef _LINUX_QCOM_GENI_SE_COMMON
#define _LINUX_QCOM_GENI_SE_COMMON
#include <linux/clk.h>
#include <linux/dma-direction.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/sched/clock.h>
#include <linux/ipc_logging.h>

#ifdef CONFIG_ARM64
#define GENI_SE_DMA_PTR_L(ptr) ((u32)ptr)
#define GENI_SE_DMA_PTR_H(ptr) ((u32)(ptr >> 32))
#else
#define GENI_SE_DMA_PTR_L(ptr) ((u32)ptr)
#define GENI_SE_DMA_PTR_H(ptr) 0
#endif

#define IPC_LOG_KPI_PAGES	(4)  // KPI IPC Log size

#define QUPV3_TEST_BUS_EN	0x204 //write 0x11
#define QUPV3_TEST_BUS_SEL	0x200 //write 0x5  [for SE index 4)
#define QUPV3_TEST_BUS_REG	0x208 //Read only reg, to be read as part of dump

#define GENI_SE_ERR(log_ctx, print, dev, x...) do { \
ipc_log_string(log_ctx, x); \
if (print) { \
	if (dev) \
		dev_err((dev), x); \
	else \
		pr_err(x); \
} \
} while (0)

#define GENI_SE_DBG(log_ctx, print, dev, x...) do { \
ipc_log_string(log_ctx, x); \
if (print) { \
	if (dev) \
		dev_dbg((dev), x); \
	else \
		pr_debug(x); \
} \
} while (0)

#define DEFAULT_BUS_WIDTH	(4)


#define DEFAULT_SE_CLK	19200
#define SPI_CORE2X_VOTE	51000
#define I2C_CORE2X_VOTE	19200
#define I3C_CORE2X_VOTE	19200
#define APPS_PROC_TO_QUP_VOTE	590000

#define SE_DMA_DEBUG_REG0		(0xE40)

#define SE_DMA_TX_PTR_L			(0xC30)
#define SE_DMA_TX_PTR_H			(0xC34)
#define SE_DMA_TX_LEN                   (0xC3C)
#define SE_DMA_TX_IRQ_EN                (0xC48)
#define SE_DMA_TX_LEN_IN                (0xC54)

#define SE_DMA_RX_PTR_L			(0xD30)
#define SE_DMA_RX_PTR_H			(0xD34)
#define SE_DMA_RX_ATTR			(0xD38)
#define SE_DMA_RX_LEN			(0xD3C)
#define SE_DMA_RX_IRQ_EN                (0xD48)
#define SE_DMA_RX_LEN_IN                (0xD54)
#define M_IRQ_ENABLE			(0x614)
#define M_CMD_ERR_STATUS		(0x624)
#define M_FW_ERR_STATUS			(0x628)
#define M_GP_LENGTH			(0x910)
#define S_GP_LENGTH			(0x914)
#define SE_DMA_DEBUG_REG0		(0xE40)
#define SE_DMA_IF_EN			(0x004)
#define GENI_CLK_CTRL_RO                (0x60)
#define SE_GENI_GENERAL_CFG		(0x10)
#define SE_DMA_TX_ATTR			(0xC38)
#define SE_DMA_TX_MAX_BURST		(0xC5C)
#define SE_DMA_RX_MAX_BURST		(0xD5C)


#define SE_DMA_TX_IRQ_EN_SET	(0xC4C)
#define SE_DMA_TX_IRQ_EN_CLR	(0xC50)

#define SE_DMA_RX_IRQ_EN_SET	(0xD4C)
#define SE_DMA_RX_IRQ_EN_CLR	(0xD50)

#define TX_FLUSH_DONE			BIT(4)
#define TX_GENI_GP_IRQ			(GENMASK(12, 5))
#define TX_GENI_CMD_FAILURE		BIT(15)
#define DMA_TX_ERROR_STATUS (TX_SBE | TX_GENI_CANCEL_IRQ | TX_GENI_CMD_FAILURE)

#define RX_GENI_GP_IRQ			GENMASK(12, 5)
#define RX_GENI_CANCEL_IRQ		BIT(14)
#define RX_GENI_CMD_FAILURE		BIT(15)
#define DMA_RX_ERROR_STATUS (RX_SBE | RX_GENI_CANCEL_IRQ | RX_GENI_CMD_FAILURE)

#define SE_HW_PARAM_2                   (0xE2C)

#define TX_GENI_CANCEL_IRQ		(BIT(14))


#define DMA_TX_ACTIVE			(BIT(0))
#define DMA_RX_ACTIVE			(BIT(1))
#define DMA_TX_STATE			(GENMASK(7, 4))
#define DMA_RX_STATE			(GENMASK(11, 8))


#define DMA_RX_IRQ_EN			(BIT(0))
#define DMA_TX_IRQ_EN			(BIT(1))
#define GENI_M_IRQ_EN			(BIT(2))
#define GENI_S_IRQ_EN			(BIT(3))

#define GENI_FW_S_REVISION_RO	(0x6C)
#define FW_REV_VERSION_MSK		(GENMASK(7, 0))


#define GENI_CFG_REG80		0x240
#define GENI_IO_MUX_0_EN	BIT(0)
#define GENI_IO_MUX_1_EN	BIT(1)


#define GEN_HW_FSM_I2C			(BIT(15))


#define IO1_SEL_TX		BIT(2)
#define IO2_DATA_IN_SEL_PAD2	GENMASK(11, 10)
#define IO3_DATA_IN_SEL_PAD2	BIT(15)

#define GSI_TX_PACK_EN          (BIT(0))
#define GSI_RX_PACK_EN          (BIT(1))
#define GSI_PRESERVE_PACK       (BIT(2))

#define HW_VER_MAJOR_MASK GENMASK(31, 28)
#define HW_VER_MAJOR_SHFT 28
#define HW_VER_MINOR_MASK GENMASK(27, 16)
#define HW_VER_MINOR_SHFT 16
#define HW_VER_STEP_MASK GENMASK(15, 0)

#define OTHER_IO_OE		BIT(12)
#define IO2_DATA_IN_SEL		BIT(11)
#define RX_DATA_IN_SEL		BIT(8)
#define IO_MACRO_IO3_SEL	(GENMASK(7, 6))
#define IO_MACRO_IO2_SEL	BIT(5)
#define IO_MACRO_IO0_SEL_BIT	BIT(0)

#define TOTAL_VOTE_INDEX	3
#define VOTE_INDEX_PROP_NAME "qcom,vote-index"
#define GENI_TO_CORE_VOTE_PROP_NAME "qcom,geni-to-core-vote"
#define CPU_TO_GENI_VOTE_PROP_NAME "qcom,cpu-to-geni-vote"
#define GENI_TO_DDR_VOTE_PROP_NAME "qcom,geni-to-ddr-vote"
#define INVALID_VOTE	0xFFFFFFFF


struct kpi_time {
	unsigned int len;
	unsigned long long time_stamp;
};

static inline int geni_se_common_resources_init(struct geni_se *se, u32 geni_to_core,
			 u32 cpu_to_geni, u32 geni_to_ddr)
{
	int ret;

	ret = geni_icc_get(se, "qup-memory");
	if (ret)
		return ret;

	se->icc_paths[GENI_TO_CORE].avg_bw = geni_to_core;
	se->icc_paths[CPU_TO_GENI].avg_bw = cpu_to_geni;
	se->icc_paths[GENI_TO_DDR].avg_bw = geni_to_ddr;

	return ret;
}


static u32 geni_se_read_vote(struct device_node *wrapper_node, enum geni_icc_path_index path,
							 u32  *vote_index_value, struct device *dev)
{
	char *vote_property_name[TOTAL_VOTE_INDEX] = {
		GENI_TO_CORE_VOTE_PROP_NAME,
		CPU_TO_GENI_VOTE_PROP_NAME,
		GENI_TO_DDR_VOTE_PROP_NAME
	};
	const __be32 *perf_values;
	int len, i, no_of_entries;
	u32 vote_value = INVALID_VOTE;

	if (path >= TOTAL_VOTE_INDEX)
		return vote_value;

	perf_values =
	of_get_property(wrapper_node, vote_property_name[path], &len);

	if (!perf_values || len % sizeof(u32)) {
		dev_err(dev, "Property %s not found or invalid\n",
			vote_property_name[path]);
		return vote_value;
	}

	no_of_entries = len / sizeof(u32);
	dev_dbg(dev, "no_of_entries: %d Property: %s\n",
		no_of_entries, vote_property_name[path]);
	if (vote_index_value[path] >= no_of_entries) {
		dev_err(dev, "Invalid Index: %d Number of values: %d property: %s\n",
			vote_index_value[path], no_of_entries,
			vote_property_name[path]);
		return vote_value;
	}

	for (i = 0; i < no_of_entries; i++) {
		if (i == vote_index_value[path]) {
			vote_value = be32_to_cpup(perf_values + i);
			dev_info(dev, "Index %d: vote_value value: %u\n", i, vote_value);
			break;
		}
	}
	return vote_value;
}


static inline int geni_se_get_common_resources(struct platform_device *pdev,
					       struct geni_se *spi_rsc)
{
	u32  vote_index_value[TOTAL_VOTE_INDEX] = {0};
	const __be32 *vote_index_list;
	int len, i, no_of_entries;
	u32 geni_to_core;
	u32 cpu_to_geni;
	u32 geni_to_ddr;
	struct device_node *wrapper_node = pdev->dev.parent->of_node;

	
	vote_index_list =
	of_get_property(pdev->dev.of_node, VOTE_INDEX_PROP_NAME, &len);

	if (!vote_index_list || len % sizeof(u32)) {
		dev_err(&pdev->dev, "Property %s not found or invalid\n",
			VOTE_INDEX_PROP_NAME);
		goto dts_err;
	}

	no_of_entries = len / sizeof(u32);
	dev_dbg(&pdev->dev, "no_of_entries: %d VOTE_INDEX_PROP_NAME: %s\n",
		no_of_entries, VOTE_INDEX_PROP_NAME);
	if (no_of_entries != TOTAL_VOTE_INDEX) {
		dev_err(&pdev->dev, "Invalid Index list Number of entries: %d property: %s\n",
			no_of_entries, VOTE_INDEX_PROP_NAME);
		goto dts_err;
	}

	for (i = 0; i < no_of_entries; i++)
		vote_index_value[i] = be32_to_cpup(vote_index_list + i);

	geni_to_core = geni_se_read_vote(wrapper_node, GENI_TO_CORE, vote_index_value, &pdev->dev);
	if (geni_to_core == INVALID_VOTE)
		goto dts_err;

	cpu_to_geni = geni_se_read_vote(wrapper_node, CPU_TO_GENI, vote_index_value, &pdev->dev);
	if (cpu_to_geni == INVALID_VOTE)
		goto dts_err;

	geni_to_ddr = geni_se_read_vote(wrapper_node, GENI_TO_DDR, vote_index_value, &pdev->dev);
	if (geni_to_ddr == INVALID_VOTE)
		goto dts_err;

	dev_dbg(&pdev->dev, "Voting with geni_to_core: %u cpu_to_geni: %u geni_to_ddr: %u\n",
		geni_to_core, cpu_to_geni, geni_to_ddr);

	return geni_se_common_resources_init(spi_rsc, geni_to_core, cpu_to_geni, geni_to_ddr);

dts_err:
	dev_dbg(&pdev->dev, "vote property not found, will load default vote\n");
	return -1;
}

static inline int geni_se_common_get_proto(void __iomem *base)
{
	int proto;

	proto = ((readl_relaxed(base + GENI_FW_REVISION_RO)
			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
	return proto;
}


static inline int geni_se_common_get_m_fw(void __iomem *base)
{
	int fw_ver_m;

	fw_ver_m = ((readl_relaxed(base + GENI_FW_REVISION_RO)
			& FW_REV_VERSION_MSK));
	return fw_ver_m;
}


static inline int geni_se_common_get_s_fw(void __iomem *base)
{
	int fw_ver_s;

	fw_ver_s = ((readl_relaxed(base + GENI_FW_S_REVISION_RO)
			& FW_REV_VERSION_MSK));
	return fw_ver_s;
}


static inline void geni_se_common_clks_off(struct clk *se_clk, struct clk *m_ahb_clk,
					struct clk *s_ahb_clk)
{
	clk_disable_unprepare(se_clk);
	clk_disable_unprepare(m_ahb_clk);
	clk_disable_unprepare(s_ahb_clk);
}


static inline int geni_se_common_clks_on(struct clk *se_clk, struct clk *m_ahb_clk,
					struct clk *s_ahb_clk)
{
	int ret;

	ret = clk_prepare_enable(m_ahb_clk);
	if (ret)
		goto clks_on_err1;

	ret = clk_prepare_enable(s_ahb_clk);
	if (ret)
		goto clks_on_err2;

	ret = clk_prepare_enable(se_clk);
	if (ret)
		goto clks_on_err3;

	return 0;

clks_on_err3:
	clk_disable_unprepare(s_ahb_clk);
clks_on_err2:
	clk_disable_unprepare(m_ahb_clk);
clks_on_err1:
	return ret;
}



static inline  void geni_write_reg(unsigned int value, void __iomem *base, int offset)
{
	return writel_relaxed(value, (base + offset));
}


static inline unsigned int geni_read_reg(void __iomem *base, int offset)
{
	return readl_relaxed(base + offset);
}



static inline int geni_se_common_iommu_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
			  void *buf, size_t size, enum dma_data_direction dir)
{
	if (!wrapper_dev)
		return -EINVAL;

	*iova = dma_map_single(wrapper_dev, buf, size, dir);
	if (dma_mapping_error(wrapper_dev, *iova))
		return -EIO;

	return 0;
}


static inline int geni_se_common_iommu_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
			    size_t size, enum dma_data_direction dir)
{
	if (!dma_mapping_error(wrapper_dev, *iova))
		dma_unmap_single(wrapper_dev, *iova,  size, dir);
	return 0;
}


static inline void *geni_se_common_iommu_alloc_buf(struct device *wrapper_dev, dma_addr_t *iova,
				size_t size)
{
	void *buf = NULL;

	if (!wrapper_dev || !iova || !size)
		return ERR_PTR(-EINVAL);

	*iova = DMA_MAPPING_ERROR;
	buf = dma_alloc_coherent(wrapper_dev, size, iova, GFP_KERNEL);
	return buf;
}


static inline int geni_se_common_iommu_free_buf(struct device *wrapper_dev, dma_addr_t *iova,
				void *buf, size_t size)
{
	if (!wrapper_dev || !iova || !buf || !size)
		return -EINVAL;

	dma_free_coherent(wrapper_dev, size, buf, *iova);
	return 0;
}


static inline void geni_se_common_rx_dma_start(void __iomem *base, int rx_len, dma_addr_t *rx_dma)
{
	if (!*rx_dma || !base || !rx_len)
		return;

	geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
	geni_write_reg(GENI_SE_DMA_PTR_L(*rx_dma), base, SE_DMA_RX_PTR_L);
	geni_write_reg(GENI_SE_DMA_PTR_H(*rx_dma), base, SE_DMA_RX_PTR_H);
	
	geni_write_reg(0, base, SE_DMA_RX_ATTR);

	
	 mb();
	geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
}


static inline void geni_se_common_get_major_minor_num(u32 hw_version,
			unsigned int *major, unsigned int *minor, unsigned int *step)
{
	*major = (hw_version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
	*minor = (hw_version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
	*step = hw_version & HW_VER_STEP_MASK;
}


static inline void test_bus_enable_per_qupv3(struct device *wrapper_dev, void *ipc)
{
	struct geni_se *geni_se_dev;

	geni_se_dev = dev_get_drvdata(wrapper_dev);
	//Enablement of test bus is required only once.
	//TEST_BUS_EN:4, TEST_BUS_REG_EN:0
	geni_write_reg(0x11, geni_se_dev->base, QUPV3_TEST_BUS_EN);
	GENI_SE_ERR(ipc, false, geni_se_dev->dev,
		    "%s: TEST_BUS_EN: 0x%x @address:0x%x\n",
		    __func__, geni_read_reg(geni_se_dev->base, QUPV3_TEST_BUS_EN),
		    (geni_se_dev->base + QUPV3_TEST_BUS_EN));
}


static inline void test_bus_select_per_qupv3(struct device *wrapper_dev, u8 test_bus_num, void *ipc)
{
	struct geni_se *geni_se_dev;

	geni_se_dev = dev_get_drvdata(wrapper_dev);

	geni_write_reg(test_bus_num, geni_se_dev->base, QUPV3_TEST_BUS_SEL);
	GENI_SE_ERR(ipc, false, geni_se_dev->dev,
		    "%s: readback TEST_BUS_SEL: 0x%x @address:0x%x\n",
		    __func__, geni_read_reg(geni_se_dev->base, QUPV3_TEST_BUS_SEL),
		    (geni_se_dev->base + QUPV3_TEST_BUS_SEL));
}


static inline void test_bus_read_per_qupv3(struct device *wrapper_dev, void *ipc)
{
	struct geni_se *geni_se_dev;

	geni_se_dev = dev_get_drvdata(wrapper_dev);
	GENI_SE_ERR(ipc, false, geni_se_dev->dev,
		    "%s: dump QUPV3_TEST_BUS_REG:0x%x\n",
		    __func__, geni_read_reg(geni_se_dev->base, QUPV3_TEST_BUS_REG));
}


static inline unsigned long long geni_capture_start_time(struct geni_se *se, void *ipc,
							 const char *func,
							 int geni_kpi_capture_enabled)
{
	struct device *dev = se->dev;
	unsigned long long start_time = 0;

	if (geni_kpi_capture_enabled) {
		start_time = sched_clock();
		GENI_SE_ERR(ipc, false, dev,
			    "%s:start at %llu nsec(%llu usec)\n", func,
			    start_time, (start_time / 1000));
	}
	return start_time;
}


static inline void geni_capture_stop_time(struct geni_se *se, void *ipc,
					  const char *func, int geni_kpi_capture_enabled,
					  unsigned long long start_time, unsigned int len,
					  unsigned int freq)
{
	struct device *dev = se->dev;
	unsigned long long exec_time = 0;

	if (geni_kpi_capture_enabled && start_time) {
		exec_time = sched_clock() - start_time;
		if (len == 0)
			GENI_SE_ERR(ipc, false, dev,
				    "%s:took %llu nsec(%llu usec)\n",
				    func, exec_time, (exec_time / 1000));
		else if (len != 0 && freq != 0)
			GENI_SE_ERR(ipc, false, dev,
				    "%s:took %llu nsec(%llu usec) for %d bytes with freq %d\n",
				    func, exec_time, (exec_time / 1000), len, freq);
		else
			GENI_SE_ERR(ipc, false, dev,
				    "%s:took %llu nsec(%llu usec) for %d bytes\n", func,
				    exec_time, (exec_time / 1000), len);
	}
}
#endif
