/*-
 * This file is provided under a dual BSD/GPLv2 license. When using or
 * redistributing this file, you may do so under either license.
 *
 *   BSD LICENSE
 *
 * Copyright 2008-2016 Freescale Semiconductor Inc.
 * Copyright (c) 2016 NXP.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * * Neither the name of the above-listed copyright holders nor the
 * names of any contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 *   GPL LICENSE SUMMARY
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") as published by the Free Software
 * Foundation, either version 2 of that License or (at your option) any
 * later version.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __RTA_FIFO_LOAD_STORE_CMD_H__
#define __RTA_FIFO_LOAD_STORE_CMD_H__

extern enum rta_sec_era rta_sec_era;

static const uint32_t fifo_load_table[][2] = {
/*1*/	{ PKA0,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_A0 },
	{ PKA1,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_A1 },
	{ PKA2,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_A2 },
	{ PKA3,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_A3 },
	{ PKB0,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_B0 },
	{ PKB1,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_B1 },
	{ PKB2,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_B2 },
	{ PKB3,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_B3 },
	{ PKA,         FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_A },
	{ PKB,         FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_B },
	{ PKN,         FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_PK_N },
	{ SKIP,        FIFOLD_CLASS_SKIP },
	{ MSG1,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_MSG },
	{ MSG2,        FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG },
	{ MSGOUTSNOOP, FIFOLD_CLASS_BOTH | FIFOLD_TYPE_MSG1OUT2 },
	{ MSGINSNOOP,  FIFOLD_CLASS_BOTH | FIFOLD_TYPE_MSG },
	{ IV1,         FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV },
	{ IV2,         FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_IV },
	{ AAD1,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_AAD },
	{ ICV1,        FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_ICV },
	{ ICV2,        FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_ICV },
	{ BIT_DATA,    FIFOLD_TYPE_BITDATA },
/*23*/	{ IFIFO,       FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_NOINFOFIFO }
};

/*
 * Allowed FIFO_LOAD input data types for each SEC Era.
 * Values represent the number of entries from fifo_load_table[] that are
 * supported.
 */
static const unsigned int fifo_load_table_sz[] = {22, 22, 23, 23,
						  23, 23, 23, 23};

static inline int
rta_fifo_load(struct program *program, uint32_t src,
	      uint64_t loc, uint32_t length, uint32_t flags)
{
	uint32_t opcode = 0;
	uint32_t ext_length = 0, val = 0;
	int ret = -EINVAL;
	bool is_seq_cmd = false;
	unsigned int start_pc = program->current_pc;

	/* write command type field */
	if (flags & SEQ) {
		opcode = CMD_SEQ_FIFO_LOAD;
		is_seq_cmd = true;
	} else {
		opcode = CMD_FIFO_LOAD;
	}

	/* Parameters checking */
	if (is_seq_cmd) {
		if ((flags & IMMED) || (flags & SGF)) {
			pr_err("SEQ FIFO LOAD: Invalid command\n");
			goto err;
		}
		if ((rta_sec_era <= RTA_SEC_ERA_5) && (flags & AIDF)) {
			pr_err("SEQ FIFO LOAD: Flag(s) not supported by SEC Era %d\n",
			       USER_SEC_ERA(rta_sec_era));
			goto err;
		}
		if ((flags & VLF) && ((flags & EXT) || (length >> 16))) {
			pr_err("SEQ FIFO LOAD: Invalid usage of VLF\n");
			goto err;
		}
	} else {
		if (src == SKIP) {
			pr_err("FIFO LOAD: Invalid src\n");
			goto err;
		}
		if ((flags & AIDF) || (flags & VLF)) {
			pr_err("FIFO LOAD: Invalid command\n");
			goto err;
		}
		if ((flags & IMMED) && (flags & SGF)) {
			pr_err("FIFO LOAD: Invalid usage of SGF and IMM\n");
			goto err;
		}
		if ((flags & IMMED) && ((flags & EXT) || (length >> 16))) {
			pr_err("FIFO LOAD: Invalid usage of EXT and IMM\n");
			goto err;
		}
	}

	/* write input data type field */
	ret = __rta_map_opcode(src, fifo_load_table,
			       fifo_load_table_sz[rta_sec_era], &val);
	if (ret < 0) {
		pr_err("FIFO LOAD: Source value is not supported. SEC Program Line: %d\n",
		       program->current_pc);
		goto err;
	}
	opcode |= val;

	if (flags & CLASS1)
		opcode |= FIFOLD_CLASS_CLASS1;
	if (flags & CLASS2)
		opcode |= FIFOLD_CLASS_CLASS2;
	if (flags & BOTH)
		opcode |= FIFOLD_CLASS_BOTH;

	/* write fields: SGF|VLF, IMM, [LC1, LC2, F1] */
	if (flags & FLUSH1)
		opcode |= FIFOLD_TYPE_FLUSH1;
	if (flags & LAST1)
		opcode |= FIFOLD_TYPE_LAST1;
	if (flags & LAST2)
		opcode |= FIFOLD_TYPE_LAST2;
	if (!is_seq_cmd) {
		if (flags & SGF)
			opcode |= FIFOLDST_SGF;
		if (flags & IMMED)
			opcode |= FIFOLD_IMM;
	} else {
		if (flags & VLF)
			opcode |= FIFOLDST_VLF;
		if (flags & AIDF)
			opcode |= FIFOLD_AIDF;
	}

	/*
	 * Verify if extended length is required. In case of BITDATA, calculate
	 * number of full bytes and additional valid bits.
	 */
	if ((flags & EXT) || (length >> 16)) {
		opcode |= FIFOLDST_EXT;
		if (src == BIT_DATA) {
			ext_length = (length / 8);
			length = (length % 8);
		} else {
			ext_length = length;
			length = 0;
		}
	}
	opcode |= (uint16_t) length;

	__rta_out32(program, opcode);
	program->current_instruction++;

	/* write pointer or immediate data field */
	if (flags & IMMED)
		__rta_inline_data(program, loc, flags & __COPY_MASK, length);
	else if (!is_seq_cmd)
		__rta_out64(program, program->ps, loc);

	/* write extended length field */
	if (opcode & FIFOLDST_EXT)
		__rta_out32(program, ext_length);

	return (int)start_pc;

 err:
	program->first_error_pc = start_pc;
	program->current_instruction++;
	return ret;
}

static const uint32_t fifo_store_table[][2] = {
/*1*/	{ PKA0,      FIFOST_TYPE_PKHA_A0 },
	{ PKA1,      FIFOST_TYPE_PKHA_A1 },
	{ PKA2,      FIFOST_TYPE_PKHA_A2 },
	{ PKA3,      FIFOST_TYPE_PKHA_A3 },
	{ PKB0,      FIFOST_TYPE_PKHA_B0 },
	{ PKB1,      FIFOST_TYPE_PKHA_B1 },
	{ PKB2,      FIFOST_TYPE_PKHA_B2 },
	{ PKB3,      FIFOST_TYPE_PKHA_B3 },
	{ PKA,       FIFOST_TYPE_PKHA_A },
	{ PKB,       FIFOST_TYPE_PKHA_B },
	{ PKN,       FIFOST_TYPE_PKHA_N },
	{ PKE,       FIFOST_TYPE_PKHA_E_JKEK },
	{ RNG,       FIFOST_TYPE_RNGSTORE },
	{ RNGOFIFO,  FIFOST_TYPE_RNGFIFO },
	{ AFHA_SBOX, FIFOST_TYPE_AF_SBOX_JKEK },
	{ MDHA_SPLIT_KEY, FIFOST_CLASS_CLASS2KEY | FIFOST_TYPE_SPLIT_KEK },
	{ MSG,       FIFOST_TYPE_MESSAGE_DATA },
	{ KEY1,      FIFOST_CLASS_CLASS1KEY | FIFOST_TYPE_KEY_KEK },
	{ KEY2,      FIFOST_CLASS_CLASS2KEY | FIFOST_TYPE_KEY_KEK },
	{ OFIFO,     FIFOST_TYPE_OUTFIFO_KEK},
	{ SKIP,      FIFOST_TYPE_SKIP },
/*22*/	{ METADATA,  FIFOST_TYPE_METADATA},
	{ MSG_CKSUM,  FIFOST_TYPE_MESSAGE_DATA2 }
};

/*
 * Allowed FIFO_STORE output data types for each SEC Era.
 * Values represent the number of entries from fifo_store_table[] that are
 * supported.
 */
static const unsigned int fifo_store_table_sz[] = {21, 21, 21, 21,
						   22, 22, 22, 23};

static inline int
rta_fifo_store(struct program *program, uint32_t src,
	       uint32_t encrypt_flags, uint64_t dst,
	       uint32_t length, uint32_t flags)
{
	uint32_t opcode = 0;
	uint32_t val = 0;
	int ret = -EINVAL;
	bool is_seq_cmd = false;
	unsigned int start_pc = program->current_pc;

	/* write command type field */
	if (flags & SEQ) {
		opcode = CMD_SEQ_FIFO_STORE;
		is_seq_cmd = true;
	} else {
		opcode = CMD_FIFO_STORE;
	}

	/* Parameter checking */
	if (is_seq_cmd) {
		if ((flags & VLF) && ((length >> 16) || (flags & EXT))) {
			pr_err("SEQ FIFO STORE: Invalid usage of VLF\n");
			goto err;
		}
		if (dst) {
			pr_err("SEQ FIFO STORE: Invalid command\n");
			goto err;
		}
		if ((src == METADATA) && (flags & (CONT | EXT))) {
			pr_err("SEQ FIFO STORE: Invalid flags\n");
			goto err;
		}
	} else {
		if (((src == RNGOFIFO) && ((dst) || (flags & EXT))) ||
		    (src == METADATA)) {
			pr_err("FIFO STORE: Invalid destination\n");
			goto err;
		}
	}
	if ((rta_sec_era == RTA_SEC_ERA_7) && (src == AFHA_SBOX)) {
		pr_err("FIFO STORE: AFHA S-box not supported by SEC Era %d\n",
		       USER_SEC_ERA(rta_sec_era));
		goto err;
	}

	/* write output data type field */
	ret = __rta_map_opcode(src, fifo_store_table,
			       fifo_store_table_sz[rta_sec_era], &val);
	if (ret < 0) {
		pr_err("FIFO STORE: Source type not supported. SEC Program Line: %d\n",
		       program->current_pc);
		goto err;
	}
	opcode |= val;

	if (encrypt_flags & TK)
		opcode |= (0x1 << FIFOST_TYPE_SHIFT);
	if (encrypt_flags & EKT) {
		if (rta_sec_era == RTA_SEC_ERA_1) {
			pr_err("FIFO STORE: AES-CCM source types not supported\n");
			ret = -EINVAL;
			goto err;
		}
		opcode |= (0x10 << FIFOST_TYPE_SHIFT);
		opcode &= (uint32_t)~(0x20 << FIFOST_TYPE_SHIFT);
	}

	/* write flags fields */
	if (flags & CONT)
		opcode |= FIFOST_CONT;
	if ((flags & VLF) && (is_seq_cmd))
		opcode |= FIFOLDST_VLF;
	if ((flags & SGF) && (!is_seq_cmd))
		opcode |= FIFOLDST_SGF;
	if (flags & CLASS1)
		opcode |= FIFOST_CLASS_CLASS1KEY;
	if (flags & CLASS2)
		opcode |= FIFOST_CLASS_CLASS2KEY;
	if (flags & BOTH)
		opcode |= FIFOST_CLASS_BOTH;

	/* Verify if extended length is required */
	if ((length >> 16) || (flags & EXT))
		opcode |= FIFOLDST_EXT;
	else
		opcode |= (uint16_t) length;

	__rta_out32(program, opcode);
	program->current_instruction++;

	/* write pointer field */
	if ((!is_seq_cmd) && (dst))
		__rta_out64(program, program->ps, dst);

	/* write extended length field */
	if (opcode & FIFOLDST_EXT)
		__rta_out32(program, length);

	return (int)start_pc;

 err:
	program->first_error_pc = start_pc;
	program->current_instruction++;
	return ret;
}

#endif /* __RTA_FIFO_LOAD_STORE_CMD_H__ */
