/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 */

#include "../include/m_ttcan.h"

/* TT Reference Message Config */
#define TTRMC_REF_PAYLOAD	(0x1 << 31)
#define TTRMC_EXT_ID		(0x1 << 30)
#define TTRMC_STD_ID_SHIFT	0x12

/* TT Operation Config */

#define TTOCF_MODE_0	0x3
#define TTOCF_MODE_1	0x2
#define TTCOF_MODE_2	0x1
#define TTOCF_EVT_TRIG	0x0

#define TTOCF_GAP		0x4
#define TTOCF_POTENTIAL_TM	0x8
#define TTOCF_LDSDL_SHIFT	0x5
#define TTOCF_IRTO_SHIFT	0x8

#define TTOCF_EXT_CLK_SYNC	0x8000
#define TTOCF_AWL_SHIFT		0x10
#define TTOCF_GBL_TIME_FLT	(0x1 << 24)
#define TTOCF_CLK_CALIB		(0x1 << 25)
#define TTOCF_EVT_POLARITY	(0x1 << 26)

enum time_master_info {
	TM_MASTER = 0,
	TM_POTENTIAL,
	TM_SLAVE,
};

inline void ttcan_clean_tt_intr(struct ttcan_controller *ttcan)
{
	ttcan_write32(ttcan, ADR_MTTCAN_TTIR, 0xFFFFFFFF);
}

void ttcan_set_ref_mesg(struct ttcan_controller *ttcan,
	u32 id, u32 rmps, u32 xtd)
{
	u32 rid;
	u32 ttrmc = (xtd << MTT_TTRMC_XTD_SHIFT) &
		MTT_TTRMC_XTD_MASK;
	ttrmc |= (rmps << MTT_TTRMC_RMPS_SHIFT) &
		MTT_TTRMC_RMPS_MASK;
	if (xtd)
		rid = (id & 0x1FFFFFFF);
	else
		rid = ((id & 0x7FF) << TTRMC_STD_ID_SHIFT);
	ttrmc |= (rid << MTT_TTRMC_RID_SHIFT) & MTT_TTRMC_RID_MASK;

	ttcan_write32(ttcan, ADR_MTTCAN_TTRMC, ttrmc);
}

void ttcan_set_tt_config(struct ttcan_controller *ttcan, u32 evtp,
	u32 ecc, u32 egtf, u32 awl, u32 eecs,
	u32 irto, u32 ldsdl, u32 tm, u32 gen, u32 om)
{
	u32 ttocf = 0;

	ttocf = (evtp << MTT_TTOCF_EVTP_SHIFT) & MTT_TTOCF_EVTP_MASK;
	ttocf |= (ecc << MTT_TTOCF_ECC_SHIFT) & MTT_TTOCF_ECC_MASK;
	ttocf |= (egtf << MTT_TTOCF_EGTF_SHIFT) & MTT_TTOCF_EGTF_MASK;
	ttocf |= (awl << MTT_TTOCF_AWL_SHIFT) & MTT_TTOCF_AWL_MASK;
	ttocf |= (eecs << MTT_TTOCF_EECS_SHIFT) & MTT_TTOCF_EECS_MASK;
	ttocf |= (irto << MTT_TTOCF_IRTO_SHIFT) & MTT_TTOCF_IRTO_MASK;
	ttocf |= (ldsdl << MTT_TTOCF_LDSDL_SHIFT) &
		MTT_TTOCF_LDSDL_MASK;
	ttocf |= (tm << MTT_TTOCF_TM_SHIFT) & MTT_TTOCF_TM_MASK;
	ttocf |= (gen << MTT_TTOCF_GEN_SHIFT) & MTT_TTOCF_GEN_MASK;
	ttocf |= (om << MTT_TTOCF_OM_SHIFT) & MTT_TTOCF_OM_MASK;

	ttcan_write32(ttcan, ADR_MTTCAN_TTOCF, ttocf);
}

inline u32 ttcan_get_ttocf(struct ttcan_controller *ttcan)
{
	return ttcan_read32(ttcan, ADR_MTTCAN_TTOCF);
}

inline void ttcan_set_tttmc(struct ttcan_controller *ttcan, u32 value)
{
	ttcan_write32(ttcan, ADR_MTTCAN_TTTMC, value);
}

inline u32 ttcan_get_tttmc(struct ttcan_controller *ttcan)
{
	return ttcan_read32(ttcan, ADR_MTTCAN_TTTMC);
}

inline void ttcan_set_txbar(struct ttcan_controller *ttcan, u32 value)
{
	ttcan_write32(ttcan, ADR_MTTCAN_TXBAR, value);
}

inline u32 ttcan_get_ttmlm(struct ttcan_controller *ttcan)
{
	return ttcan_read32(ttcan, ADR_MTTCAN_TTMLM);
}

inline u32 ttcan_get_cccr(struct ttcan_controller *ttcan)
{
	return ttcan_read32(ttcan, ADR_MTTCAN_CCCR);
}

inline u32 ttcan_get_ttost(struct ttcan_controller *ttcan)
{
	return ttcan_read32(ttcan, ADR_MTTCAN_TTOST);
}

static bool validate_matrix_cyc_cnt(u32 cyc_num)
{
	if (cyc_num && ((cyc_num + 1) & cyc_num))
		return false;
	return true;
}

int ttcan_set_matrix_limits(struct ttcan_controller *ttcan,
			    u32 entt, u32 txew, u32 css, u32 ccm)
{
	u32 ttmlm = 0;

	if (!validate_matrix_cyc_cnt(ccm) ||
	    (txew > 15) || (css > 2) || (entt > 4095)) {
		pr_err("%s: Invalid parameters\n", __func__);
		return -1;
	}
	ttmlm = (entt << MTT_TTMLM_ENTT_SHIFT) & MTT_TTMLM_ENTT_MASK;
	ttmlm |= (txew << MTT_TTMLM_TXEW_SHIFT) & MTT_TTMLM_TXEW_MASK;
	ttmlm |= (css << MTT_TTMLM_CSS_SHIFT) & MTT_TTMLM_CSS_MASK;
	ttmlm |= (ccm << MTT_TTMLM_CCM_SHIFT) & MTT_TTMLM_CCM_MASK;

	ttcan_write32(ttcan, ADR_MTTCAN_TTMLM, ttmlm);

	return 0;
}

#define MTTCAN_TT_TIMEOUT 1000
int ttcan_set_tur_config(struct ttcan_controller *ttcan, u16 denominator,
			 u16 numerator, int local_timing_enable)
{
	int timeout = MTTCAN_TT_TIMEOUT;
	u32 turcf = ttcan_read32(ttcan, ADR_MTTCAN_TURCF);

	if (!denominator && local_timing_enable) {
		pr_err("%s: Invalid Denominator value\n", __func__);
		return -1;
	}

	if (turcf & MTT_TURCF_ELT_MASK) {
		turcf &= ~(MTT_TURCF_ELT_MASK);
		ttcan_write32(ttcan, ADR_MTTCAN_TURCF, turcf);
	}

	if (!local_timing_enable)
		return 0;

	if (turcf & MTT_TURCF_ELT_MASK) {
		while ((turcf & MTT_TURCF_ELT_MASK) && timeout) {
			udelay(1);
			timeout--;
			turcf = ttcan_read32(ttcan, ADR_MTTCAN_TURCF);
		}
		if (!timeout) {
			pr_err("%s: TURCF access is locked\n", __func__);
			return -1;
		}
	}

	/* TBD: Take care when the DEN or NUM is programmed
	 * outside TT Conf mode
	 */

	turcf = (numerator << MTT_TURCF_NCL_SHIFT) & MTT_TURCF_NCL_MASK;
	turcf |= (denominator << MTT_TURCF_DC_SHIFT) & MTT_TURCF_DC_MASK;
	turcf |= MTT_TURCF_ELT_MASK;

	ttcan_write32(ttcan, ADR_MTTCAN_TURCF, turcf);

	return 0;
}

void ttcan_set_trigger_mem_conf(struct ttcan_controller *ttcan)
{
	u32 tttmc = 0;
	u32 rel_start_addr = ttcan->mram_cfg[MRAM_TMC].off >> 2;
	u8 elem_num = ttcan->mram_cfg[MRAM_TMC].num;

	if (elem_num > 64)
		elem_num = 64;

	tttmc = (elem_num << MTT_TTTMC_TME_SHIFT) & MTT_TTTMC_TME_MASK;
	tttmc |= (rel_start_addr << MTT_TTTMC_TMSA_SHIFT) &
		MTT_TTTMC_TMSA_MASK;

	ttcan_write32(ttcan, ADR_MTTCAN_TTTMC, tttmc);
}
