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



#include <linux/io.h>
#include <mali_kbase.h>
#include "mali_kbase_cpu_mt6873.h"

#define HZ_IN_MHZ (1000000)

#define CORETILE_EXPRESS_A9X4_SCC_START	(0x100E2000)
#define MOTHERBOARD_SYS_CFG_START	(0x10000000)
#define SYS_CFGDATA_OFFSET		(0x000000A0)
#define SYS_CFGCTRL_OFFSET		(0x000000A4)
#define SYS_CFGSTAT_OFFSET		(0x000000A8)

#define SYS_CFGCTRL_START_BIT_VALUE		(1 << 31)
#define READ_REG_BIT_VALUE			(0 << 30)
#define DCC_DEFAULT_BIT_VALUE			(0 << 26)
#define SYS_CFG_OSC_FUNC_BIT_VALUE		(1 << 20)
#define SITE_DEFAULT_BIT_VALUE			(1 << 16)
#define BOARD_STACK_POS_DEFAULT_BIT_VALUE	(0 << 12)
#define DEVICE_DEFAULT_BIT_VALUE		(2 <<  0)
#define SYS_CFG_COMPLETE_BIT_VALUE		(1 <<  0)
#define SYS_CFG_ERROR_BIT_VALUE			(1 <<  1)

#define FEED_REG_BIT_MASK			(0x0F)
#define FCLK_PA_DIVIDE_BIT_SHIFT		(0x03)
#define FCLK_PB_DIVIDE_BIT_SHIFT		(0x07)
#define FCLK_PC_DIVIDE_BIT_SHIFT		(0x0B)
#define AXICLK_PA_DIVIDE_BIT_SHIFT		(0x0F)
#define AXICLK_PB_DIVIDE_BIT_SHIFT		(0x13)

/* the following three values used for reading
 * HBI value of the LogicTile daughterboard
 */
#define VE_MOTHERBOARD_PERIPHERALS_SMB_CS7 (0x10000000)
#define VE_SYS_PROC_ID1_OFFSET (0x00000088)
#define VE_LOGIC_TILE_HBI_MASK (0x00000FFF)

#define IS_SINGLE_BIT_SET(val, pos) (val&(1<<pos))

/**
 * Values used for determining the GPU frequency based on the LogicTile type
 * Used by the function kbase_get_platform_logic_tile_type
 */
#define VE_VIRTEX6_GPU_FREQ_MIN 5000
#define VE_VIRTEX6_GPU_FREQ_MAX 5000
#define VE_VIRTEX7_GPU_FREQ_MIN 40000
#define VE_VIRTEX7_GPU_FREQ_MAX 40000
#define VE_DEFAULT_GPU_FREQ_MIN 5000
#define VE_DEFAULT_GPU_FREQ_MAX 5000


#define CPU_CLOCK_SPEED_UNDEFINED (0)

static u32 cpu_clock_speed = CPU_CLOCK_SPEED_UNDEFINED;

static DEFINE_RAW_SPINLOCK(syscfg_lock);
/**
 * kbase_get_vendor_specific_cpu_clock_speed -Retrieves the CPU clock speed
 * @cpu_clock - the value of CPU clock speed in MHz
 *
 * Returns 0 on success, error code otherwise.
 *
 * The implementation is platform specific.
*/
int kbase_get_vexpress_cpu_clock_speed(u32 *cpu_clock)
{
	int err = 0;
	u32 reg_val = 0;
	u32 osc2_value = 0;
	u32 pa_divide = 0;
	u32 pb_divide = 0;
	u32 pc_divide = 0;
	void __iomem *syscfg_reg = NULL;
	void __iomem *scc_reg = NULL;

	if (CPU_CLOCK_SPEED_UNDEFINED != cpu_clock_speed) {
		*cpu_clock = cpu_clock_speed;
		return 0;
	}

	/* Init the value in case something goes wrong */
	*cpu_clock = 0;

	/* Map CPU register into virtual memory */
	syscfg_reg = ioremap(MOTHERBOARD_SYS_CFG_START, 0x1000);
	if (syscfg_reg == NULL) {
		err = -EIO;
		goto syscfg_reg_map_failed;
	}

	scc_reg = ioremap(CORETILE_EXPRESS_A9X4_SCC_START, 0x1000);
	if (scc_reg == NULL) {
		err = -EIO;
		goto scc_reg_map_failed;
	}

	raw_spin_lock(&syscfg_lock);

	/* Read SYS regs - OSC2 */
	reg_val = readl(syscfg_reg + SYS_CFGCTRL_OFFSET);

	/* Check if there is any other undergoing request */
	if (reg_val & SYS_CFGCTRL_START_BIT_VALUE) {
		err = -EBUSY;
		goto ongoing_request;
	}
	/* Reset the CGFGSTAT reg */
	writel(0, (syscfg_reg + SYS_CFGSTAT_OFFSET));

	writel(SYS_CFGCTRL_START_BIT_VALUE | READ_REG_BIT_VALUE |
			DCC_DEFAULT_BIT_VALUE |
			SYS_CFG_OSC_FUNC_BIT_VALUE |
			SITE_DEFAULT_BIT_VALUE |
			BOARD_STACK_POS_DEFAULT_BIT_VALUE |
			DEVICE_DEFAULT_BIT_VALUE,
			(syscfg_reg + SYS_CFGCTRL_OFFSET));
	/* Wait for the transaction to complete */
	while (!(readl(syscfg_reg + SYS_CFGSTAT_OFFSET) &
			SYS_CFG_COMPLETE_BIT_VALUE))
		;
	/* Read SYS_CFGSTAT Register to get the status of submitted
	 * transaction
	 */
	reg_val = readl(syscfg_reg + SYS_CFGSTAT_OFFSET);

	if (reg_val & SYS_CFG_ERROR_BIT_VALUE) {
		/* Error while setting register */
		err = -EIO;
		goto set_reg_error;
	}

	osc2_value = readl(syscfg_reg + SYS_CFGDATA_OFFSET);
	/* Read the SCC CFGRW0 register */
	reg_val = readl(scc_reg);

	/*
	 * Select the appropriate feed:
	 * CFGRW0[0] - CLKOB
	 * CFGRW0[1] - CLKOC
	 * CFGRW0[2] - FACLK (CLK)B FROM AXICLK PLL)
	 */
	/* Calculate the  FCLK */
	if (IS_SINGLE_BIT_SET(reg_val, 0)) {
		/* CFGRW0[0] - CLKOB */
		/* CFGRW0[6:3] */
		pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				FCLK_PA_DIVIDE_BIT_SHIFT)) >>
				FCLK_PA_DIVIDE_BIT_SHIFT);
		/* CFGRW0[10:7] */
		pb_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				FCLK_PB_DIVIDE_BIT_SHIFT)) >>
				FCLK_PB_DIVIDE_BIT_SHIFT);
		*cpu_clock = osc2_value * (pa_divide + 1) / (pb_divide + 1);
	} else if (IS_SINGLE_BIT_SET(reg_val, 1)) {
		/* CFGRW0[1] - CLKOC */
		/* CFGRW0[6:3] */
		pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				FCLK_PA_DIVIDE_BIT_SHIFT)) >>
				FCLK_PA_DIVIDE_BIT_SHIFT);
		/* CFGRW0[14:11] */
		pc_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				FCLK_PC_DIVIDE_BIT_SHIFT)) >>
				FCLK_PC_DIVIDE_BIT_SHIFT);
		*cpu_clock = osc2_value * (pa_divide + 1) / (pc_divide + 1);
	} else if (IS_SINGLE_BIT_SET(reg_val, 2)) {
		/* CFGRW0[2] - FACLK */
		/* CFGRW0[18:15] */
		pa_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				AXICLK_PA_DIVIDE_BIT_SHIFT)) >>
				AXICLK_PA_DIVIDE_BIT_SHIFT);
		/* CFGRW0[22:19] */
		pb_divide = ((reg_val & (FEED_REG_BIT_MASK <<
				AXICLK_PB_DIVIDE_BIT_SHIFT)) >>
				AXICLK_PB_DIVIDE_BIT_SHIFT);
		*cpu_clock = osc2_value * (pa_divide + 1) / (pb_divide + 1);
	} else {
		err = -EIO;
	}

set_reg_error:
ongoing_request:
	raw_spin_unlock(&syscfg_lock);
	*cpu_clock /= HZ_IN_MHZ;

	if (!err)
		cpu_clock_speed = *cpu_clock;

	iounmap(scc_reg);

scc_reg_map_failed:
	iounmap(syscfg_reg);

syscfg_reg_map_failed:

	return err;
}

/**
 * kbase_get_platform_logic_tile_type -  determines which LogicTile type
 * is used by Versatile Express
 *
 * When platform_config build parameter is specified as vexpress, i.e.,
 * platform_config=vexpress, GPU frequency may vary dependent on the
 * particular platform. The GPU frequency depends on the LogicTile type.
 *
 * This function determines which LogicTile type is used by the platform by
 * reading the HBI value of the daughterboard which holds the LogicTile:
 *
 * 0x217 HBI0217 Virtex-6
 * 0x192 HBI0192 Virtex-5
 * 0x247 HBI0247 Virtex-7
 *
 * Return: HBI value of the logic tile daughterboard, zero if not accessible
 */
static u32 kbase_get_platform_logic_tile_type(void)
{
	void __iomem *syscfg_reg = NULL;
	u32 sys_procid1 = 0;

	syscfg_reg = ioremap(VE_MOTHERBOARD_PERIPHERALS_SMB_CS7 + VE_SYS_PROC_ID1_OFFSET, 4);
	if (NULL != syscfg_reg) {
		sys_procid1 = readl(syscfg_reg);
		iounmap(syscfg_reg);
	}

	return sys_procid1 & VE_LOGIC_TILE_HBI_MASK;
}

u32 kbase_get_platform_min_freq(void)
{
	u32 ve_logic_tile = kbase_get_platform_logic_tile_type();

	switch (ve_logic_tile) {
	case 0x217:
		/* Virtex 6, HBI0217 */
		return VE_VIRTEX6_GPU_FREQ_MIN;
	case 0x247:
		/* Virtex 7, HBI0247 */
		return VE_VIRTEX7_GPU_FREQ_MIN;
	default:
		/* all other logic tiles, i.e., Virtex 5 HBI0192
		 * or unsuccessful reading from the platform -
		 * fall back to some default value
		 */
		return VE_DEFAULT_GPU_FREQ_MIN;
	}
}

u32 kbase_get_platform_max_freq(void)
{
	u32 ve_logic_tile = kbase_get_platform_logic_tile_type();

	switch (ve_logic_tile) {
	case 0x217:
		/* Virtex 6, HBI0217 */
		return VE_VIRTEX6_GPU_FREQ_MAX;
	case 0x247:
		/* Virtex 7, HBI0247 */
		return VE_VIRTEX7_GPU_FREQ_MAX;
	default:
		/* all other logic tiles, i.e., Virtex 5 HBI0192
		 * or unsuccessful reading from the platform -
		 * fall back to some default value
		 */
		return VE_DEFAULT_GPU_FREQ_MAX;
	}
}
