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

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

int ttcan_read_txevt_ram(struct ttcan_controller *ttcan, u32 read_addr,
				struct mttcan_tx_evt_element *txevt)
{
	void __iomem *msg_addr = read_addr + ttcan->mram_vbase;
	if (!txevt)
		return -1;

	txevt->f0 = readl(msg_addr);
	txevt->f1 = readl(msg_addr + CAN_WORD_IN_BYTES);

	return 0;
}

int ttcan_read_rx_msg_ram(struct ttcan_controller *ttcan, u64 read_addrs,
			  struct ttcanfd_frame *ttcanfd)
{
	u32 i = 0, byte_index;
	u32 msg_data;
	void __iomem *addr_in_msg_ram = read_addrs + ttcan->mram_vbase;
	int bytes_to_read = CANFD_MAX_DLEN;

	if (!ttcanfd)
		return -1;

	ttcanfd->flags |= CAN_DIR_RX;

	while (bytes_to_read) {
		msg_data =
		    readl(addr_in_msg_ram + (i * CAN_WORD_IN_BYTES));

		switch (i) {
		case 0:
			ttcanfd->can_id = 0;
			ttcanfd->flags = 0;

			if (msg_data & RX_BUF_XTD) {
				/* Extended Frame */
				ttcanfd->can_id =  CAN_FMT |
				    ((msg_data & RX_BUF_EXTID_MASK) &
				   CAN_EXT_ID_MASK);
			} else {
				ttcanfd->can_id =
					((msg_data & RX_BUF_STDID_MASK) >>
					RX_BUF_STDID_SHIFT) & CAN_STD_ID_MASK;
			}

			if (msg_data & RX_BUF_RTR)
				ttcanfd->can_id |= CAN_RTR;

			if (msg_data & RX_BUF_ESI)
				ttcanfd->flags |= CAN_ESI_FLAG;
			break;
		case 1:
			ttcanfd->d_len =
			    ttcan_dlc2len((msg_data & RX_BUF_DLC_MASK)
					  >> RX_BUF_DLC_SHIFT);
			bytes_to_read = ttcanfd->d_len;

			if (msg_data & RX_BUF_FDF)
				ttcanfd->flags |= CAN_FD_FLAG;

			if (msg_data & RX_BUF_BRS)
				ttcanfd->flags |= CAN_BRS_FLAG;
			ttcanfd->tstamp = msg_data & RX_BUF_RXTS_MASK;
			break;
		default:
			byte_index = (i - 2) * CAN_WORD_IN_BYTES;
			switch	(bytes_to_read) {
			default:
			case 4:
				ttcanfd->data[byte_index + 3] =
					(msg_data >> 24) & 0xFF;
				fallthrough;
			case 3:
				ttcanfd->data[byte_index + 2] =
					(msg_data >> 16) & 0xFF;
				fallthrough;
			case 2:
				ttcanfd->data[byte_index + 1] =
					(msg_data >> 8) & 0xFF;
				fallthrough;
			case 1:
				ttcanfd->data[byte_index + 0] =
					 msg_data & 0xFF;
			}

			if (bytes_to_read >= 4)
				bytes_to_read -= 4;
			else
				bytes_to_read = 0;
			pr_debug("%s addr %p received 0x%x\n", __func__,
				addr_in_msg_ram +
				(i * CAN_WORD_IN_BYTES), msg_data);
			break;
		}
		i++;
	}
	pr_debug("%s:received ID(0x%x) %s %s(%s)\n", __func__,
		(ttcanfd->can_id & CAN_FMT) ?
			(ttcanfd->can_id & CAN_EXT_ID_MASK) :
			(ttcanfd->can_id & CAN_STD_ID_MASK),
		(ttcanfd->can_id & CAN_FMT) ? "XTD" : "STD",
		(ttcanfd->flags & CAN_FD_FLAG) ? "FD" : "NON-FD",
		(ttcanfd->flags & CAN_BRS_FLAG) ? "BRS" : "NOBRS");

	return i;
}

int ttcan_write_tx_msg_ram(struct ttcan_controller *ttcan, u32 write_addrs,
			   struct ttcanfd_frame *ttcanfd, u8 index)
{
	u32 msg_data, idx;
	int bytes_to_write;
	void __iomem *addr_in_msg_ram = write_addrs + ttcan->mram_vbase;

	/* T0 */
	if (ttcanfd->can_id & CAN_FMT)
		msg_data = (ttcanfd->can_id & CAN_EXT_ID_MASK) | TX_BUF_XTD;
	else
		msg_data =
		    (ttcanfd->can_id & CAN_STD_ID_MASK) << TX_BUF_STDID_SHIFT;

	if (ttcanfd->can_id & CAN_RTR)
		msg_data |= TX_BUF_RTR;

	/*  This flag is ORed with error passive flag while sending */
	if (ttcanfd->flags & CAN_ESI_FLAG)
		msg_data |= TX_BUF_ESI;

	pr_debug("T0: addr %p msg %x\n", addr_in_msg_ram, msg_data);
	writel(msg_data, addr_in_msg_ram);

	/* T1 */
	msg_data =
	    (ttcan_len2dlc(ttcanfd->d_len) << TX_BUF_DLC_SHIFT) &
	    TX_BUF_DLC_MASK;

	if (ttcan->tx_config.evt_q_num)
		msg_data |= TX_BUF_EFC;

	if (ttcanfd->flags & CAN_FD_FLAG)
		msg_data |= TX_BUF_FDF;

	if (ttcanfd->flags & CAN_BRS_FLAG)
		msg_data |= TX_BUF_BRS;

	msg_data |= index <<  TX_BUF_MM_SHIFT;

	pr_debug("%s:buf_id(%d):- %s(%s)\n", __func__, index,
		(ttcanfd->flags & CAN_FD_FLAG) ? "FD" : "NON-FD",
		(ttcanfd->flags & CAN_BRS_FLAG) ? "BRS" : "NOBRS");

	pr_debug("T1: addr %p msg %x\n", (addr_in_msg_ram+CAN_WORD_IN_BYTES),
		msg_data);
	writel(msg_data, (addr_in_msg_ram + CAN_WORD_IN_BYTES));

	bytes_to_write = ttcanfd->d_len;

	idx = 0;

	while (bytes_to_write > 0) {
		msg_data = 0;
		switch (bytes_to_write) {
		default:
		case 4:
			msg_data = ttcanfd->data[idx + 3] << 24;
			fallthrough;
		case 3:
			msg_data += ttcanfd->data[idx + 2] << 16;
			fallthrough;
		case 2:
			msg_data += ttcanfd->data[idx + 1] << 8;
			fallthrough;
		case 1:
			msg_data += ttcanfd->data[idx + 0] << 0;
		}

		pr_debug("T2: addr %p msg %x\n", (addr_in_msg_ram +
				(((idx >> 2) + 2) * CAN_WORD_IN_BYTES)),
				msg_data);
		writel(msg_data, (addr_in_msg_ram +
		       (((idx >> 2) + 2) * CAN_WORD_IN_BYTES)));
		idx += 4;
		bytes_to_write -= 4;
	}

	return idx;
}

void ttcan_set_std_id_filter(struct ttcan_controller *ttcan, void *std_shadow,
				int filter_index, u8 sft, u8 sfec, u32 sfid1,
				u32 sfid2)
{
	u32 filter_elem = 0;
	void __iomem *filter_addr =
	    ttcan->mram_vbase + ttcan->mram_cfg[MRAM_SIDF].off;
	u32 filter_offset =
	    (filter_index * SIDF_ELEM_SIZE);

	filter_elem = (sfid2 << MTT_STD_FLTR_SFID2_SHIFT) &
		MTT_STD_FLTR_SFID2_MASK;
	filter_elem |= (sfid1 << MTT_STD_FLTR_SFID1_SHIFT) &
		MTT_STD_FLTR_SFID1_MASK;
	filter_elem |= (sfec <<  MTT_STD_FLTR_SFEC_SHIFT) &
		MTT_STD_FLTR_SFEC_MASK;
	filter_elem |= (sft << MTT_STD_FLTR_SFT_SHIFT) & MTT_STD_FLTR_SFT_MASK;

	pr_debug("%s %p\n", __func__, (filter_addr + filter_offset));
	memcpy((char *)std_shadow + filter_offset, &filter_elem,
		SIDF_ELEM_SIZE);
	writel(filter_elem, (void __iomem *)(filter_addr + filter_offset));
}

void ttcan_prog_std_id_fltrs(struct ttcan_controller *ttcan, void *std_shadow)
{
	int idx;
	u32 list_size = ttcan->mram_cfg[MRAM_SIDF].num;
	void __iomem *filter_addr = ttcan->mram_vbase +
					ttcan->mram_cfg[MRAM_SIDF].off;
	for (idx = 0; idx < list_size; idx++) {
		u32 offset = idx * SIDF_ELEM_SIZE;
		writel(*(u32 *)((u8 *)std_shadow + offset),
			(void __iomem *)(filter_addr + offset));
	}
}

u32 ttcan_get_std_id_filter(struct ttcan_controller *ttcan, int idx)
{
	void __iomem *filter_addr = ttcan->mram_vbase +
					ttcan->mram_cfg[MRAM_SIDF].off;

	u32 filter_offset = idx * SIDF_ELEM_SIZE;
	return readl(filter_addr + filter_offset);
}

void ttcan_prog_xtd_id_fltrs(struct ttcan_controller *ttcan, void *xtd_shadow)
{
	int idx;
	u32 list_size = ttcan->mram_cfg[MRAM_XIDF].num;
	void __iomem *filter_addr = ttcan->mram_vbase +
					ttcan->mram_cfg[MRAM_XIDF].off;

	for (idx = 0; idx < list_size; idx++) {
		u32 offset = idx * XIDF_ELEM_SIZE;

		writel(*(u32 *)((u8 *)xtd_shadow + offset),
			(void __iomem *)(filter_addr + offset));
		writel(*(u32 *)((u8 *)xtd_shadow + offset + CAN_WORD_IN_BYTES),
			(void __iomem *)(filter_addr + offset +
			CAN_WORD_IN_BYTES));
	}
}

void ttcan_set_xtd_id_filter(struct ttcan_controller *ttcan, void *xtd_shadow,
				int filter_index, u8 eft, u8 efec, u32 efid1,
				u32 efid2)
{
	struct mttcan_xtd_id_filt_element xfilter_elem;
	void __iomem *filter_addr =
	    ttcan->mram_vbase + ttcan->mram_cfg[MRAM_XIDF].off;
	u32 filter_offset = filter_index * XIDF_ELEM_SIZE;

	xfilter_elem.f0 = (efec << MTT_XTD_FLTR_F0_EFEC_SHIFT) &
		MTT_XTD_FLTR_F0_EFEC_MASK;
	xfilter_elem.f0 |= (efid1 << MTT_XTD_FLTR_F0_EFID1_SHIFT) &
		MTT_XTD_FLTR_F0_EFID1_MASK;
	xfilter_elem.f1 = (eft << MTT_XTD_FLTR_F1_EFT_SHIFT) &
		MTT_XTD_FLTR_F1_EFT_MASK;
	xfilter_elem.f1 |= (efid2 << MTT_XTD_FLTR_F1_EFID2_SHIFT) &
		MTT_XTD_FLTR_F1_EFID2_MASK;

	memcpy((char *)xtd_shadow + filter_offset, &xfilter_elem,
		XIDF_ELEM_SIZE);
	writel(xfilter_elem.f0,
	       (void __iomem *)(filter_addr + filter_offset));
	writel(xfilter_elem.f1,
	       (void __iomem *)(filter_addr + filter_offset +
				CAN_WORD_IN_BYTES));
	pr_debug("%s %x %p\n", __func__, xfilter_elem.f0,
			(filter_addr + filter_offset));
	pr_debug("%s %x %p\n", __func__, xfilter_elem.f1,
			(filter_addr + filter_offset + CAN_WORD_IN_BYTES));
}

u64 ttcan_get_xtd_id_filter(struct ttcan_controller *ttcan, int idx)
{
	u64 xtd;
	void __iomem *filter_addr = ttcan->mram_vbase +
		ttcan->mram_cfg[MRAM_XIDF].off;
	u32 offset = idx * XIDF_ELEM_SIZE;

	xtd = ((u64) readl(filter_addr + offset + CAN_WORD_IN_BYTES)) << 32;
	xtd |= readl(filter_addr + offset);
	return xtd;
}

u64 ttcan_get_trigger_mem(struct ttcan_controller *ttcan, int idx)
{
	u64 trig;
	void __iomem *addr = ttcan->mram_vbase + ttcan->mram_cfg[MRAM_TMC].off;
	u32 offset = idx * TRIG_ELEM_SIZE;

	trig = ((u64) readl(addr + offset + CAN_WORD_IN_BYTES)) << 32;
	trig |= readl(addr + offset);
	return trig;
}

void ttcan_prog_trigger_mem(struct ttcan_controller *ttcan, void *tmc_shadow)
{
	int idx;
	void __iomem *filter_addr = ttcan->mram_vbase +
			ttcan->mram_cfg[MRAM_TMC].off;
	u32 list_size = ttcan->mram_cfg[MRAM_TMC].num;

	for (idx = 0; idx < list_size; idx++) {
		u32 offset = idx * TRIG_ELEM_SIZE;

		writel(*(u32 *)((char *)tmc_shadow + idx),
			(void __iomem *)(filter_addr + offset));
		writel(*(u32 *)((char *)tmc_shadow + idx),
			(void __iomem *)(filter_addr + offset +
			CAN_WORD_IN_BYTES));
	}
}

int ttcan_set_trigger_mem(struct ttcan_controller *ttcan, void *tmc_shadow,
				int trig_index, u16 time_mark, u16 cycle_code,
				u8 tmin, u8 tmex, u16 trig_type, u8 filter_type,
				u8 mesg_num)
{
	struct mttcan_trig_mem_element trig_elem;
	void __iomem *trig_mem_addr = ttcan->mram_vbase +
					ttcan->mram_cfg[MRAM_TMC].off;
	u32 idx = trig_index * TRIG_ELEM_SIZE;

	if (trig_index > 63) {
		pr_err("%s: Incorrect Index\n", __func__);
		return -1;
	}

	if (cycle_code > 127) {
		pr_err("%s: Invalid cycle code\n", __func__);
		return -1;
	}

	/* TBD: ASC is disabled - Hardcoded */
	/* Mesg. Status Count is set 0 */
	trig_elem.f0 = (time_mark << MTT_TRIG_ELE_F0_TM_SHIFT) &
		MTT_TRIG_ELE_F0_TM_MASK;
	trig_elem.f0 |= cycle_code << MTT_TRIG_ELE_F0_CC_SHIFT &
		MTT_TRIG_ELE_F0_CC_MASK;
	/* ASC = 0 */;
	trig_elem.f0 |= (tmin <<  MTT_TRIG_ELE_F0_TMIN_SHIFT) &
		MTT_TRIG_ELE_F0_TMIN_MASK;
	trig_elem.f0 |= (tmex << MTT_TRIG_ELE_F0_TMEX_SHIFT) &
		MTT_TRIG_ELE_F0_TMEX_MASK;
	trig_elem.f0 |= (trig_type << MTT_TRIG_ELE_F0_TYPE_SHIFT) &
		MTT_TRIG_ELE_F0_TYPE_MASK;
	trig_elem.f1 = (filter_type << MTT_TRIG_ELE_F1_FTYPE_SHIFT) &
		MTT_TRIG_ELE_F1_FTYPE_MASK;
	trig_elem.f1 |= (mesg_num << MTT_TRIG_ELE_F1_MNR_SHIFT) &
		MTT_TRIG_ELE_F1_MNR_MASK;

	memcpy((char *)tmc_shadow + idx, &trig_elem, TRIG_ELEM_SIZE);
	writel(trig_elem.f0, (void __iomem *)(trig_mem_addr + idx));
	writel(trig_elem.f1, (void __iomem *)(trig_mem_addr + idx +
		CAN_WORD_IN_BYTES));

	return 0;
}

int ttcan_mesg_ram_config(struct ttcan_controller *ttcan,
	u32 *arr, u32 *tx_conf, u32 *rx_conf)
{

	ttcan->mram_cfg[MRAM_SIDF].off = arr[0];
	ttcan->mram_cfg[MRAM_SIDF].num = arr[1];

	ttcan->mram_cfg[MRAM_XIDF].off = ttcan->mram_cfg[MRAM_SIDF].off +
			ttcan->mram_cfg[MRAM_SIDF].num * SIDF_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_XIDF].num = arr[2];

	ttcan->mram_cfg[MRAM_RXF0].off = ttcan->mram_cfg[MRAM_XIDF].off +
			ttcan->mram_cfg[MRAM_XIDF].num * XIDF_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_RXF0].num = arr[3];

	ttcan->mram_cfg[MRAM_RXF1].off = ttcan->mram_cfg[MRAM_RXF0].off +
			ttcan->mram_cfg[MRAM_RXF0].num * MAX_RXB_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_RXF1].num = arr[4];

	ttcan->mram_cfg[MRAM_RXB].off = ttcan->mram_cfg[MRAM_RXF1].off +
			ttcan->mram_cfg[MRAM_RXF1].num * MAX_RXB_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_RXB].num = arr[5];

	ttcan->mram_cfg[MRAM_TXE].off = ttcan->mram_cfg[MRAM_RXB].off +
			ttcan->mram_cfg[MRAM_RXB].num * MAX_RXB_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_TXE].num = arr[6];

	ttcan->mram_cfg[MRAM_TXB].off = ttcan->mram_cfg[MRAM_TXE].off +
			ttcan->mram_cfg[MRAM_TXE].num * TX_EVENT_FIFO_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_TXB].num = arr[7];

	ttcan->mram_cfg[MRAM_TMC].off = ttcan->mram_cfg[MRAM_TXB].off +
			ttcan->mram_cfg[MRAM_TXB].num * MAX_TXB_ELEM_SIZE;
	ttcan->mram_cfg[MRAM_TMC].num = arr[8];

	if ((ttcan->mram_size <=
		ttcan->mram_cfg[MRAM_TMC].off + ttcan->mram_cfg[MRAM_TMC].num *
		       TRIG_ELEM_SIZE - ttcan->mram_cfg[MRAM_SIDF].off)) {
		pr_err("%s: Incorrect config for Message RAM\n", __func__);
		return -EINVAL;
	}

	if ((tx_conf[0] + tx_conf[1] > ttcan->mram_cfg[MRAM_TXB].num)) {
		pr_err("%s: Incorrect tx-config in dt.\n", __func__);
		return -EINVAL;
	}

	if ((tx_conf[0] != 0) && (tx_conf[1] != 0)) {
		pr_err("%s: Incorrect tx-config in dt.\n", __func__);
		pr_err("Using both Tx buf and Fifo not allowed (Errata 21)\n");
		return -EINVAL;
	}

	ttcan->tx_config.ded_buff_num = tx_conf[TX_CONF_TXB];
	ttcan->tx_config.fifo_q_num = tx_conf[TX_CONF_TXQ];
	ttcan->tx_config.flags = tx_conf[TX_CONF_QMODE];
	ttcan->e_size.tx_buffer = TXB_ELEM_HEADER_SIZE + tx_conf[TX_CONF_BSIZE];
	ttcan->tx_config.dfs = get_dfs(tx_conf[TX_CONF_BSIZE]);
	ttcan->rx_config.rxb_dsize = rx_conf[RX_CONF_RXB];
	ttcan->rx_config.rxq0_dsize = rx_conf[RX_CONF_RXF0];
	ttcan->rx_config.rxq1_dsize = rx_conf[RX_CONF_RXF1];

	pr_info("\t Message RAM Configuration\n"
		"\t| base addr   |0x%08lx|\n\t| sidfc_flssa |0x%08x|\n\t| xidfc_flesa |0x%08x|\n"
		"\t| rxf0c_f0sa  |0x%08x|\n\t| rxf1c_f1sa  |0x%08x|\n\t| rxbc_rbsa   |0x%08x|\n"
		"\t| txefc_efsa  |0x%08x|\n\t| txbc_tbsa   |0x%08x|\n\t| tmc_tmsa    |0x%08x|\n"
		"\t| mram size   |0x%08x|\n",
		ttcan->mram_base, ttcan->mram_cfg[MRAM_SIDF].off,
		ttcan->mram_cfg[MRAM_XIDF].off, ttcan->mram_cfg[MRAM_RXF0].off,
		ttcan->mram_cfg[MRAM_RXF1].off, ttcan->mram_cfg[MRAM_RXB].off,
		ttcan->mram_cfg[MRAM_TXE].off, ttcan->mram_cfg[MRAM_TXB].off,
		ttcan->mram_cfg[MRAM_TMC].off, ttcan->mram_size);
	return 0;
}

void ttcan_mesg_ram_init(struct ttcan_controller *ttcan)
{
	u32 offset;

	for (offset = 0; offset < ttcan->mram_size;
	     offset += CAN_WORD_IN_BYTES) {
		writel(0, ttcan->mram_vbase + offset);
	}
}
