// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */

#include <linux/delay.h>

#include "mhal_demux_common.h"

//-------------------------------------------------------------------------------------------------
//  Debug Constants and Macros
//-------------------------------------------------------------------------------------------------
#define MHAL_LABLE										"[MHAL_DEMUX_COMMON]"

//#define MHAL_DEMUX_COMMON_DEBUG_ENABLE

//-------------------------------------------------------------------------------------------------
//  Constants
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Macros
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Structs and Enums
//-------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------
//  Variables
//-------------------------------------------------------------------------------------------------
mtk_demux_riu_banks (*demux_bank_offsets);
static spinlock_t indirect_lock;

//-------------------------------------------------------------------------------------------------
//  Internal Functions
//-------------------------------------------------------------------------------------------------
#ifdef MHAL_DEMUX_COMMON_DEBUG_ENABLE
static int _mhal_demux_common_print_bytes(u8 *pu8Addr, u8 u8Size)
{
	u8 u8Index = 0;

	if (pu8Addr == NULL) {
		MHAL_DEMUX_ERR("pu8Addr is NULL.\n");

		return MHAL_DEMUX_FAIL;
	}

	for (u8Index = 0; u8Index < u8Size; u8Index++)
		MHAL_DEMUX_DEBUG("[%u]0x%x ", (unsigned int)u8Index, (int)pu8Addr[u8Index]);

	MHAL_DEMUX_DEBUG("\n");

	return MHAL_DEMUX_SUCCESS;
}

static int _mhal_demux_common_reg16_print_bytes(const char *szTitle, u16 u32Value)
{
	int iRet = MHAL_DEMUX_SUCCESS;
	u8 u8Size = sizeof(u32Value);
	u8 *pu8Value = (u8 *)&u32Value;

	if ((szTitle != NULL) && (strlen(szTitle) > 0))
		MHAL_DEMUX_DEBUG("[%s]\n", szTitle);
	iRet = _mhal_demux_common_print_bytes(pu8Value, u8Size);

	return iRet;
}

static int _mhal_demux_common_reg32_print_bytes(const char *szTitle, u32 u32Value)
{
	int iRet = MHAL_DEMUX_SUCCESS;
	u8 u8Size = sizeof(u32Value);
	u8 *pu8Value = (u8 *)&u32Value;

	if ((szTitle != NULL) && (strlen(szTitle) > 0))
		MHAL_DEMUX_DEBUG("[%s]\n", szTitle);
	iRet = _mhal_demux_common_print_bytes(pu8Value, u8Size);

	return iRet;
}
#else
static int _mhal_demux_common_print_bytes(u8 *pu8Addr, u8 u8Size)
{
	return MHAL_DEMUX_SUCCESS;
}

static int _mhal_demux_common_reg16_print_bytes(u8 *pu8Addr, u8 u8Size)
{
	return MHAL_DEMUX_SUCCESS;
}

static int _mhal_demux_common_reg32_print_bytes(u8 *pu8Addr, u8 u8Size)
{
	return MHAL_DEMUX_SUCCESS;
}
#endif //#ifdef MHAL_DEMUX_COMMON_DEBUG_ENABLE

//-------------------------------------------------------------------------------------------------
//  Public Functions
//-------------------------------------------------------------------------------------------------
void mhal_demux_common_init(mtk_demux_riu_banks *riu_banks)
{
	demux_bank_offsets = riu_banks;

	spin_lock_init(&indirect_lock);
}

void mhal_demux_common_exit(void)
{
	demux_bank_offsets = NULL;
}

bool send_mcu_cmd(u32 cmd, u32 *pmsg0, u32 *pmsg1)
{
	u32 i = 0;

	if (pmsg0)
		RegWrite4B(REG_TSP1_BASE + REG_0000_TSP1, REG_TSP1_BASE + REG_0004_TSP1, *pmsg0);
	if (pmsg1)
		RegWrite4B(REG_TSP1_BASE + REG_01F0_TSP1, REG_TSP1_BASE + REG_01F4_TSP1, *pmsg1);
	RegWrite4B(REG_TSP0_BASE + REG_0100_TSP0, REG_TSP0_BASE + REG_0104_TSP0, cmd);

	while ((i < MCU_CMD_RETRY_TIMES) &&
		  (RegRead4B(REG_TSP0_BASE + REG_0100_TSP0,
		   REG_TSP0_BASE + REG_0104_TSP0) != 0)) {
		i++;
		usleep_range(MCU_CMD_RETRY_MIN, MCU_CMD_RETRY_MAX);
	}

	if (i >= MCU_CMD_RETRY_TIMES)
		return false;

	if (pmsg0)
		*pmsg0 = RegRead4B(REG_TSP1_BASE + REG_0000_TSP1, REG_TSP1_BASE + REG_0004_TSP1);

	if (pmsg1)
		*pmsg1 = RegRead4B(REG_TSP1_BASE + REG_01F0_TSP1, REG_TSP1_BASE + REG_01F4_TSP1);

	return true;
}

void indirect_write(u32 OR_addr, u32 value)
{
	unsigned long flags;

	spin_lock_irqsave(&indirect_lock, flags);

	RegWrite4B(REG_TSP0_BASE + REG_0124_TSP0, REG_TSP0_BASE + REG_0128_TSP0, OR_addr);
	RegWrite4B(REG_TSP0_BASE + REG_012C_TSP0, REG_TSP0_BASE + REG_0130_TSP0, value);
	vIO32Write2B(REG_TSP0_BASE + REG_0120_TSP0, IND_WRITE_MASK);

	while (u4IO32ReadFld(REG_TSP0_BASE + REG_0120_TSP0, TSP0_REG_IND32_RW_START_0120) != 0);

	spin_unlock_irqrestore(&indirect_lock, flags);
}

u32 indirect_read(u32 OR_addr)
{
	unsigned long flags;
	u32 value;

	spin_lock_irqsave(&indirect_lock, flags);

	RegWrite4B(REG_TSP0_BASE + REG_0124_TSP0, REG_TSP0_BASE + REG_0128_TSP0, OR_addr);
	vIO32Write2B(REG_TSP0_BASE + REG_0120_TSP0, IND_READ_MASK);

	while (u4IO32ReadFld(REG_TSP0_BASE + REG_0120_TSP0, TSP0_REG_IND32_RW_START_0120) != 0);

	value = RegRead4B(REG_TSP0_BASE + REG_0134_TSP0, REG_TSP0_BASE + REG_0138_TSP0);

	spin_unlock_irqrestore(&indirect_lock, flags);

	return value;
}

void reg32_write(REG16 __iomem *offset, u32 value)
{
	u16 value_low = (u16)value;
	u16 value_high = (u16)(value >> 16);

	_mhal_demux_common_reg32_print_bytes("value", value);

	writew(value_low, offset);
	writew(value_high, offset + 4);

}

u32 reg32_read(REG16 __iomem *offset)
{
	u16 value_low = readw(offset);
	u16 value_high = readw(offset + 4);

	return ((((u32)value_high) << 16) + (u32)value_low);
}

void reg32_set_mask(REG16 __iomem *offset, u32 mask)
{
	u32 value;

	_mhal_demux_common_reg32_print_bytes("mask", mask);

	value = reg32_read(offset);
	value |= mask;
	reg32_write(offset, value);
}

void reg32_clear_mask(REG16 __iomem *offset, u32 mask)
{
	u32 value;

	_mhal_demux_common_reg32_print_bytes("mask", mask);

	value = reg32_read(offset);
	value &= ~mask;
	reg32_write(offset, value);
}

void reg32_mask_write(REG16 __iomem *offset, u32 mask, u32 value)
{
	u32 temp;

	_mhal_demux_common_reg32_print_bytes("value", value);
	_mhal_demux_common_reg32_print_bytes("mask", mask);

	temp = reg32_read(offset);
	temp &= ~mask;
	temp |= (mask & value);
	reg32_write(offset, temp);
}

u16 reg32_mask_read(REG16 __iomem *offset, u32 mask, u32 shift)
{
	u16 value;

	value = reg32_read(offset);

	return (value & mask) >> shift;
}

void reg16_write(REG16 __iomem *offset, u16 value)
{
	_mhal_demux_common_reg16_print_bytes("value", value);

	writew(value, offset);
}

u16 reg16_read(REG16 __iomem *offset)
{
	return readw(offset);
}

void reg16_set_bit(REG16 __iomem *offset, u8 nr)
{
	u16 value;

	value = readw(offset);
	value |= (1 << nr);
	writew(value, offset);
}

void reg16_clear_bit(REG16 __iomem *offset, u8 nr)
{
	u16 value;

	value = readw(offset);
	value &= ~(1 << nr);
	writew(value, offset);
}

bool reg16_test_bit(REG16 __iomem *offset, u8 nr)
{
	if (readw(offset) & (1 << nr))
		return true;
	else
		return false;
}

void reg16_set_mask(REG16 __iomem *offset, u16 mask)
{
	u16 value;

	_mhal_demux_common_reg16_print_bytes("set mask", mask);

	value = readw(offset);
	value |= mask;
	writew(value, offset);
}

void reg16_clear_mask(REG16 __iomem *offset, u16 mask)
{
	u16 value;

	_mhal_demux_common_reg16_print_bytes("clear mask", mask);

	value = readw(offset);
	value &= ~mask;
	writew(value, offset);
}

void reg16_mask_write(REG16 __iomem *offset, u16 mask, u16 value)
{
	u16 temp;

	_mhal_demux_common_reg16_print_bytes("mask", mask);
	_mhal_demux_common_reg16_print_bytes("value", value);

	temp = readw(offset);
	temp &= ~mask;
	temp |= (mask & value);
	writew(temp, offset);
}

u16 reg16_mask_read(REG16 __iomem *offset, u16 mask, u16 shift)
{
	u16 value;

	value = readw(offset);

	return (value & mask) >> shift;
}

int reg_base_get(enum reg_base reg_base, REG16 __iomem **base)
{
	int ret = MHAL_DEMUX_SUCCESS;

	switch (reg_base) {
	case REG_BASE_TSP_MIN...REG_BASE_TSP_MAX:
		*base = REG_BASE_TSP(reg_base - REG_BASE_TSP_MIN);
		break;
	case REG_BASE_TSO_MIN...REG_BASE_TSO_MAX:
		*base = REG_BASE_TSO(reg_base - REG_BASE_TSO_MIN);
		break;
	case REG_BASE_TLV_MIN...REG_BASE_TLV_MAX:
		*base = REG_BASE_TLV(reg_base - REG_BASE_TLV_MIN);
		break;
	case REG_BASE_PAS_MIN...REG_BASE_PAS_MAX:
		*base = REG_BASE_PAS(reg_base - REG_BASE_PAS_MIN);
		break;
	case REG_BASE_CLKGEN_MIN...REG_BASE_CLKGEN_MAX:
		// TODO
		*base = REG_CLKGEN0_BASE;
		break;
	case REG_BASE_TS_SAMPLE_MIN...REG_BASE_TS_SAMPLE_MAX:
		// TODO
		*base = REG_TS_SAMPLE_BASE;
		break;
	case REG_BASE_ALP_MIN...REG_BASE_ALP_MAX:
		*base = REG_BASE_ALP(reg_base - REG_BASE_ALP_MIN);
		break;
	default:
		MHAL_DEMUX_ERR(
			"%s: %d: reg_base(%d) is not supported.\n",
			__func__,
			__LINE__,
			reg_base);
		ret = MHAL_DEMUX_INVALID_PARAMETER;
		break;
	}

	return ret;
}
