/**
 * @file  trng.c
 * @brief TRNG API for Fastcall driver
 *
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include "drStd.h"
#include "drMobicore.h"
#include "DrApi/DrApiFastCall.h"

#include "trng.h"
#include "regs-sss.h"

uint32_t rng_seed_buf[8];
uint32_t rng_seed_size;

#if TBASE_API_LEVEL >= 5
extern addr_t sss_sfr_va;
#endif

uint32_t rng_get_seed(
	fastcall_registers_t regs
) {
	uint32_t reg;
	int trycnt = 0;

	if (rng_seed_size < 2) {
#if TBASE_API_LEVEL >= 5
		write_sfr_value((uint32_t)sss_sfr_va + TRNG_CLKDIV, TRNG_DIV_RNG0);
		write_sfr_value((uint32_t)sss_sfr_va + TRNG_CTRL, TRNG_RNGEN);
		write_sfr_value((uint32_t)sss_sfr_va + TRNG_POST_CTRL, TRNG_PPEN | TRNG_VN);
		reg = read_sfr_value((uint32_t)sss_sfr_va + TRNG_ONLINE_CTRL);
		write_sfr_value((uint32_t)sss_sfr_va + TRNG_ONLINE_CTRL, reg & ~TRNG_OTEN);
		write_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_CTRL, TRNG_FIFOPTR_256BIT);

		while(1) {
			if (!read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_CTRL))
				break;

			if (trycnt++ > 0x100000)
				return MC_FC_RET_ERR_RETRY;
		}

		rng_seed_buf[0] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_0);
		rng_seed_buf[1] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_1);
		rng_seed_buf[2] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_2);
		rng_seed_buf[3] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_3);
		rng_seed_buf[4] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_4);
		rng_seed_buf[5] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_5);
		rng_seed_buf[6] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_6);
		rng_seed_buf[7] = read_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_7);

		write_sfr_value((uint32_t)sss_sfr_va + TRNG_FIFO_CTRL, TRNG_FIFOPTR_256BIT);
#else //TBASE_API_LEVEL >= 5
		write_sfr_value(SSS_SFR_VA + TRNG_CLKDIV, TRNG_DIV_RNG0);
		write_sfr_value(SSS_SFR_VA + TRNG_CTRL, TRNG_RNGEN);
		write_sfr_value(SSS_SFR_VA + TRNG_POST_CTRL, TRNG_PPEN | TRNG_VN);
		reg = read_sfr_value(SSS_SFR_VA + TRNG_ONLINE_CTRL);
		write_sfr_value(SSS_SFR_VA + TRNG_ONLINE_CTRL, reg & ~TRNG_OTEN);
		write_sfr_value(SSS_SFR_VA + TRNG_FIFO_CTRL, TRNG_FIFOPTR_256BIT);

		while(1) {
			if (!read_sfr_value(SSS_SFR_VA + TRNG_FIFO_CTRL))
				break;

			if (trycnt++ > 0x100000)
				return MC_FC_RET_ERR_RETRY;
		}

		rng_seed_buf[0] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_0);
		rng_seed_buf[1] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_1);
		rng_seed_buf[2] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_2);
		rng_seed_buf[3] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_3);
		rng_seed_buf[4] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_4);
		rng_seed_buf[5] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_5);
		rng_seed_buf[6] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_6);
		rng_seed_buf[7] = read_sfr_value(SSS_SFR_VA + TRNG_FIFO_7);

		write_sfr_value(SSS_SFR_VA + TRNG_FIFO_CTRL, TRNG_FIFOPTR_256BIT);
#endif //TBASE_API_LEVEL >= 5
		rng_seed_size = 8;
	}

	regs[2] = rng_seed_buf[--rng_seed_size];
	rng_seed_buf[rng_seed_size] = 0;
	regs[3] = rng_seed_buf[--rng_seed_size];
	rng_seed_buf[rng_seed_size] = 0;

	return MC_FC_RET_OK;
}
