/**
 * @file  opt.c
 * @brief OTP controller 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 "DrApi/DrApiCommon.h"
#include "DrApi/DrApiFastCall.h"
#include "drMobicore.h"
#include "otp.h"

/************************************************************************/
/*                                                                      */
/* OTP controller driver functions                                      */
/*                                                                      */
/************************************************************************/

/* Initialize the OTP memory */
static uint32_t
otp_cmd_init(void)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t retry_count = 0;

	/* set OTP initialization command */
	reg_data = read_sfr_value(OTP_CON_CONTROL);
	write_sfr_value(OTP_CON_CONTROL, (reg_data | OTP_INIT_CMD_ON));

	/* polling the status reg */
	while (1) {
		if ((read_sfr_value(OTP_INT_STATUS) & OTP_INT_DONE_MASK) ==
		    OTP_INT_DONE_ON) {
			rv = RV_SUCCESS;
			break;
		}

		retry_count++;

		if (retry_count > TIME_OUT)
			return RV_OTP_INIT_TIME_OUT;
	}

	/* clear the interrupt status */
	reg_data = read_sfr_value(OTP_INT_STATUS);
	write_sfr_value(OTP_INT_STATUS, (reg_data | OTP_INT_DONE_ON));

	return rv;
}

/* change the status of the OTP memory to standby */
static uint32_t
otp_cmd_standby(void)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t retry_count = 0;

	/* set standby command */
	reg_data = read_sfr_value(OTP_CON_CONTROL);
	write_sfr_value(OTP_CON_CONTROL, (reg_data | OTP_STANDBY_CMD_ON));

	/* polling the status reg */
	while (1) {
		if ((read_sfr_value(OTP_INT_STATUS) & OTP_STANDBY_DONE_MASK) ==
		    OTP_STANDBY_DONE_ON) {
			rv = RV_SUCCESS;
			break;
		}

		retry_count++;

		if (retry_count > TIME_OUT)
			return RV_OTP_STANDBY_TIME_OUT;
	}

	/* clear the interrupt status */
	reg_data = read_sfr_value(OTP_INT_STATUS);
	write_sfr_value(OTP_INT_STATUS, (reg_data | OTP_STANDBY_DONE_ON));

	return rv;
}

/* function to read 1 word of OTP memory at once */
static uint32_t
otp_cmd_read(uint32_t address, uint32_t *read_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t retry_count = 0;
	uint32_t access_region;

	access_region = address & OTP_IF_REGION_MASK;
	if (access_region == OTP_IF_REGION_HWONLY)
		return RV_OTP_READ_UNACCESSIBLE_REGION;

	/* 1. set address */
	/* OTP_IF: program data[31], address [15:0] */
	reg_data = (address & OTP_IF_ADDR_MASK_READ);
	write_sfr_value(OTP_IF, reg_data);

	/* 2. set read command */
	reg_data = read_sfr_value(OTP_CON_CONTROL);
	write_sfr_value(OTP_CON_CONTROL, (reg_data | OTP_READ_CMD_ON));

	/* 3. check read status */
	while (1) {
		reg_data = read_sfr_value(OTP_INT_STATUS);

		/* check read done */
		if ((reg_data & OTP_READ_DONE_MASK) == OTP_READ_DONE_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_READ_DONE_ON));
			rv = RV_SUCCESS;
			break;
		}

		/* Check secure fail */
		if ((reg_data & OTP_SECURE_FAIL_MASK) == OTP_SECURE_FAIL_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_SECURE_FAIL_ON));
			return RV_OTP_READ_SECURE_FAIL;
		}

		retry_count ++;

		if (retry_count > TIME_OUT)
			return RV_OTP_READ_TIME_OUT;
	}

	/* read SECURE DATA [bit [14:13]= 1:0 or 1:1] */
	/* or read NON SECURE DATA [bit [14:13]= 0:0] */
	if (access_region == OTP_IF_REGION_SECURE)
		*read_data = read_sfr_value(OTP_SECURE_READ_DATA);
	else
		*read_data = read_sfr_value(OTP_NONSECURE_READ_DATA);

	return rv;
}

/* otp_cmd_program : program all bank */
static uint32_t
otp_cmd_program(uint32_t address, uint32_t program_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t retry_count = 0;

	/* 0. setting Tpw valuer: 0x9 --> 0x11 */
	reg_data = read_sfr_value(OTP_CON_TIME_PARA_1);
	write_sfr_value(OTP_CON_TIME_PARA_1, ((reg_data & 0xFFFF0000) | 0x11));

	/* 1. set address */
	/* OTP_IF: program data[31],address [14:0] */
	reg_data = (address & OTP_IF_ADDR_MASK) | ((program_data & 0x1) << 31);
	write_sfr_value(OTP_IF, reg_data);

	/* 2. set program command */
	reg_data = read_sfr_value(OTP_CON_CONTROL);
	write_sfr_value(OTP_CON_CONTROL, (reg_data | OTP_PROGRAM_CMD_ON));

	/* 3. check read status */
	while (1) {

		reg_data = read_sfr_value(OTP_INT_STATUS);

		/* Check program done */
		if ((reg_data & OTP_PROGRAM_DONE_MASK) == OTP_PROGRAM_DONE_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_PROGRAM_DONE_ON));
			rv = RV_SUCCESS;
			break;
		}

		/* Check program_lock */
		if ((reg_data & OTP_PROGRAM_LOCK_MASK) == OTP_PROGRAM_LOCK_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_PROGRAM_LOCK_ON));
			return RV_OTP_PROGRAM_LOCK;
		}

		/* Check program fail */
		if ((reg_data & OTP_PROGRAM_FAIL_MASK) == OTP_PROGRAM_FAIL_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_PROGRAM_FAIL_ON));
			return RV_OTP_PROGRAM_FAIL;
		}

		/* Check secure fail */
		if ((reg_data & OTP_SECURE_FAIL_MASK) == OTP_SECURE_FAIL_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_SECURE_FAIL_ON));
			return RV_OTP_PROGRAM_SECURE_FAIL;
		}

		retry_count ++;

		if (retry_count > TIME_OUT)
			return RV_OTP_PROGRAM_TIME_OUT;
	}

	return rv;
}

/* otp_cmd_lock_program : program additional region */
static uint32_t
otp_cmd_lock_program(uint32_t address, uint32_t program_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t retry_count = 0;

	// 0.new additon(20140304) setting Tpw valuer: 0x9 --> 0x11
	reg_data = read_sfr_value(OTP_CON_TIME_PARA_1);
	write_sfr_value(OTP_CON_TIME_PARA_1,((reg_data & 0xFFFF0000) | 0x11));

	// 1. set address
	// OTP_IF: program data[31],address [14:0]
	reg_data = (address & OTP_IF_ADDR_MASK) | ((program_data & 0x1) << 31);
	write_sfr_value(OTP_IF, reg_data);

	// 2. set lock program command
	reg_data = read_sfr_value(OTP_CON_CONTROL);
	write_sfr_value(OTP_CON_CONTROL, (reg_data | OTP_LOCK_PROGRAM_CMD_ON));

	// 3. check read status
	while (1) {
		reg_data = read_sfr_value(OTP_INT_STATUS);
		/* Check lock program done */
		if ((reg_data & OTP_LOCK_PROGRAM_DONE_MASK) ==
		    OTP_LOCK_PROGRAM_DONE_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_LOCK_PROGRAM_DONE_ON));
			rv = RV_SUCCESS;
			break;
		}
		/* Check program fail */
		if ((reg_data & OTP_LOCK_PROGRAM_FAIL_MASK) ==
		    OTP_LOCK_PROGRAM_FAIL_ON) {
			write_sfr_value(OTP_INT_STATUS,
					(reg_data | OTP_LOCK_PROGRAM_FAIL_ON));
			return RV_OTP_LOCK_PROGRAM_FAIL;
		}

		retry_count ++;

		if (retry_count > TIME_OUT)
			return RV_OTP_LOCK_PROGRAM_TIME_OUT;
	}

	return rv;
}

/* enable interrupt */
void
otp_set_interrupt(uint32_t enable)
{
	if (enable == 1) {
		write_sfr_value(OTP_INT_EN,
				OTP_INIT_DONE_INT_ON |
				OTP_READ_DONE_INT_ON |
				OTP_PROGRAM_DONE_INT_ON |
				OTP_STANDBY_DONE_INT_ON |
				OTP_LOCK_PROGRAM_DONE_INT_ON |
				OTP_PROGRAM_FAIL_INT_ON |
				OTP_LOCK_PROGRAM_FAIL_INT_ON);
	} else {
		write_sfr_value(OTP_INT_EN,
				OTP_INIT_DONE_INT_OFF |
				OTP_READ_DONE_INT_OFF |
				OTP_PROGRAM_DONE_INT_OFF |
				OTP_STANDBY_DONE_INT_OFF |
				OTP_LOCK_PROGRAM_DONE_INT_OFF |
				OTP_PROGRAM_FAIL_INT_OFF |
				OTP_LOCK_PROGRAM_FAIL_INT_OFF);
	}
}

/* read interrupt status */
uint32_t
otp_get_interrupt(void)
{
	return read_sfr_value(OTP_INT_STATUS);
}

/* reset interrupt status */
void
otp_reset_interrupt(uint32_t data)
{
	write_sfr_value(OTP_INT_STATUS, data);
}

/*************************************************************************/
/*                                                                       */
/* API functions for OTP program, sensing, locking                       */
/*                                                                       */
/*************************************************************************/
/*
 * function to program 1 bit value
 * address: address to program 1 bit value
 * program_data: should be '1' for programing
 */
uint32_t
otp_write_one_bit(uint32_t address, uint32_t program_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;

	rv = otp_read_one_bit(address, &reg_data);
	if (rv != RV_SUCCESS)
		return rv;

	if (reg_data)
		return RV_SUCCESS;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_program(address, program_data);
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to program 1 byte value
 * address: start address to program  8 bits value, need to be aligned
 * program_data: one byte value to program
 */
uint32_t
otp_write_one_byte(uint32_t address, uint32_t program_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t count;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	for (count = 0; count < 8; count++) {

		if ((program_data >> count) & 0x1) {

			rv = otp_cmd_program(address + count, 1);
			if (rv != RV_SUCCESS)
				return rv;
		}
	}

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to program 1 word value
 * address: start address to program 32 bits value, need to be aligned
 * program_data: one byte value to program
 */
uint32_t
otp_write_one_word(uint32_t address, uint32_t program_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t count;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_read(address, &reg_data);
	if (rv != RV_SUCCESS)
		return rv;

	for (count = 0; count < 32; count++) {

		if ((((program_data ^ reg_data) & program_data) >> count) & 0x1) {

			rv = otp_cmd_program(address + count, 1);
			if (rv != RV_SUCCESS)
				return rv;
		}
	}

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to program multiple bits
 * address: address to program, need to be aligned
 * program_data: byte arrary including bit stream
 * bit_len: bit length to be programed
 */
uint32_t
otp_write_byte_array(uint32_t address, uint8_t *program_data, uint32_t bit_len)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t write_count;
	uint32_t write_data;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	for (write_count = 0; write_count < bit_len; write_count++) {
		/* Clear bits of [31:1] */
		write_data = (program_data[write_count] & 0x01);

		rv = otp_cmd_program(address + write_count, write_data);
		if (rv!= RV_SUCCESS)
			return rv;
	}

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to read one word
 * address: need to be aligned
 * read_data: pointer to store the read word value
 */
uint32_t
otp_read_one_bit(uint32_t address, uint32_t *read_data)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data;
	uint32_t offset;
	offset = 0;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	offset = (address % 0x20);

	rv = otp_cmd_read(address, &reg_data);
	if (rv != RV_SUCCESS)
		return rv;
	else
		*read_data = ((reg_data >> offset) & 0x1);

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to read one word
 * address: need to be aligned
 * read_data: pointer to store the read word value
 */
uint32_t
otp_read_one_word(uint32_t address, uint32_t *read_data)
{
	uint32_t rv = RV_SUCCESS;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_read(address, read_data);
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to read bit string
 * address: need to be aligned
 * bit_len: count of bit to be read
 * read_data: pointer to store the read value as byte string
 */
uint32_t
otp_read_byte_array(uint32_t address, uint8_t *read_data, uint32_t bit_len)
{
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data = 0;
	uint32_t bit_count = 0;
	uint32_t word_count;
	uint32_t shift_count;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	for (word_count = 0; word_count < (bit_len / 32); word_count++) {

		rv = otp_cmd_read(address + (word_count * 32), &reg_data);
		if (rv != RV_SUCCESS)
			return rv;
		/* save result */
		for (shift_count = 0; shift_count < 32; shift_count ++) {
			read_data[bit_count++] =
				(uint8_t)((reg_data >> shift_count) & 0x1);
		}
	}

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to apply program lock to the specific bank
 * bank_index: bank number be to locked
 */
uint32_t
otp_program_lock(uint32_t bank_index)
{
	uint32_t rv = RV_SUCCESS;

	rv = otp_cmd_init();
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_lock_program(OTP_LOCK_KEY_BASE + bank_index, 1);
	if (rv != RV_SUCCESS)
		return rv;

	rv = otp_cmd_standby();
	if (rv != RV_SUCCESS)
		return rv;

	return rv;
}

/*
 * function to check locking of sepecific memory bank
 * bank_index: OTP bank number to be checked
 */
uint32_t
otp_check_bank_lock(uint32_t bank_index)
{
	uint32_t reg_data = 0;
	uint32_t shfit_num;

	shfit_num = bank_index;

	// check bank 20 locking
	if (shfit_num > 31) {
		shfit_num =  shfit_num - OTP_LOCK_KEY20_AREA_BASE;
		reg_data = read_sfr_value(OTP_CUSTOM_LOCK0);

	} else
		reg_data = read_sfr_value(OTP_LOCK0);

	reg_data = (reg_data >> shfit_num) & 0x1;

	return reg_data;
}

/************************************************************************/
/*                                                                      */
/* FastCall handler function                                            */
/*                                                                      */
/************************************************************************/
uint32_t otp_control(
	fastcall_registers_t regs
) {
	uint32_t rv = RV_SUCCESS;
	uint32_t reg_data, reg_tmp;
	uint32_t otp_command_id;

	reg_data = 0;
	reg_tmp = 0;
	otp_command_id = regs[2];

	switch (otp_command_id) {
/*----------------------------------------------------------------------*/
/* program CUSTOM key                                                   */
/*----------------------------------------------------------------------*/
	case CMD_W_USE_OEM_KEY:
		regs[1] = otp_write_one_bit(OTP_USE_OEM_KEY_ADDR, 0x1);
		break;

	case CMD_W_BAN_OEM_KEY:
		regs[1] = otp_write_one_bit(OTP_BAN_OEM_KEY_ADDR, 0x1);
		break;

	case CMD_W_SKIP_AES:
		regs[1] = otp_write_one_bit(OTP_SKIP_AES_ADDR, 0x1);
		break;

	case CMD_W_BAN_APFA:
		regs[1] = otp_write_one_bit(OTP_BAN_APFA_ADDR, 0x1);
		break;

	case CMD_W_JTAG_LOCK:
		regs[1] = otp_write_one_bit(OTP_JTAG_SOFT_LOCK_ADDR, 0x1);
		break;

	case CMD_W_PREORDER:
		reg_data = regs[3];
#if defined(CONFIG_EXYNOS5433) || defined(CONFIG_EXYNOS7420) || defined(CONFIG_EXYNOS7580)
		rv = otp_read_one_word(OTP_CHIPID_96_ADDR, &reg_tmp);
		if (rv != RV_SUCCESS) {
			regs[1] = rv;
			break;
		} else if (((reg_tmp & 0xFF0000) >> 16) < 0x11) {
			if (reg_data) {
					rv = otp_write_one_bit(OTP_SWCRYPTO_ADDR, 0x1);
					if (rv != RV_SUCCESS) {
						regs[1] = rv;
						break;
					}
			}
		}
#endif
		regs[1] = otp_write_one_word(OTP_CUSTOM_KEY_ADDR,
									 ((reg_data << 16) & 0xFF0000));
		break;

	case CMD_W_MODEL_ID:
		regs[1] = otp_write_one_word(OTP_MODEL_ID_ADDR, regs[1]);
		break;

	case CMD_W_COMMERCIAL:
		rv = otp_read_one_bit(OTP_TEST_ADDR, &reg_data);
		if (rv != RV_SUCCESS)
			regs[1] = rv;
		else if (reg_data == 0x1)
			regs[1] = RV_OTP_TEST_BIT_SET_ALREADY;
		else
			regs[1] = otp_write_one_bit(OTP_COMMERCIAL_ADDR, 0x1);
		break;

	case CMD_W_TEST:
		rv = otp_read_one_bit(OTP_COMMERCIAL_ADDR, &reg_data);
		if (rv != RV_SUCCESS)
			regs[1] = rv;
		else if (reg_data == 0x1)
			regs[1] = RV_OTP_COMMERCIAL_BIT_SET_ALREADY;
		else
			regs[1] = otp_write_one_bit(OTP_TEST_ADDR, 0x1);
		break;

	case CMD_W_WARRANTY:
			regs[1] = otp_write_one_bit(OTP_WARRANTY_ADDR, 1);
			break;
/*----------------------------------------------------------------------*/
/* program anti-rollback counter                                        */
/*----------------------------------------------------------------------*/
	case CMD_W_NANTIRBK0_0:
		rv = otp_write_one_word(OTP_ANTIRBK_ADDR0, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_ANTIRBK_ADDR1, regs[3]);
		break;

	case CMD_W_NANTIRBK0_1:
		rv = otp_write_one_word(OTP_ANTIRBK_ADDR2, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_ANTIRBK_ADDR3, regs[3]);
		break;

	case CMD_W_NANTIRBK1_0:
		rv = otp_write_one_word(OTP_ANTIRBK_ADDR4, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_ANTIRBK_ADDR5, regs[3]);
		break;

	case CMD_W_NANTIRBK1_1:
		regs[1] = otp_write_one_word(OTP_ANTIRBK_ADDR6, regs[1]);
		break;

	case CMD_W_SANTIRBK:
		regs[1] = otp_write_one_word(OTP_ANTIRBK_SEC_ADDR, regs[1]);
		break;
/*----------------------------------------------------------------------*/
/* program OEM_KEY to SECKEY bank                                       */
/*----------------------------------------------------------------------*/
	case CMD_W_OEM_KEY0_0:
		rv = otp_write_one_word(OTP_SECKEY_ADDR0, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_SECKEY_ADDR1, regs[3]);
		break;

	case CMD_W_OEM_KEY0_1:
		rv = otp_write_one_word(OTP_SECKEY_ADDR2, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_SECKEY_ADDR3, regs[3]);
		break;

	case CMD_W_OEM_KEY1_0:
		rv = otp_write_one_word(OTP_SECKEY_ADDR4, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_SECKEY_ADDR5, regs[3]);
		break;

	case CMD_W_OEM_KEY1_1:
		rv = otp_write_one_word(OTP_SECKEY_ADDR6, regs[1]);
		regs[1] = (rv != RV_SUCCESS) ? rv:
				otp_write_one_word(OTP_SECKEY_ADDR7, regs[3]);
		break;

	case CMD_W_ETC:
		if (regs[3] > 31) {
			regs[1] = RV_OTP_INVALID_BIT_INDEX;
			break;
			}
		regs[1] = otp_write_one_bit(OTP_CUSTOM_ETC_ADDR + regs[3], 1);
		break;
/*----------------------------------------------------------------------*/
/* FastCall handling for OTP reading                                    */
/*----------------------------------------------------------------------*/
	case CMD_R_USE_OEM_KEY:
		regs[1] = otp_read_one_bit(OTP_USE_OEM_KEY_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_BAN_OEM_KEY:
		regs[1] = otp_read_one_bit(OTP_BAN_OEM_KEY_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_SKIP_AES:
		regs[1] = otp_read_one_bit(OTP_SKIP_AES_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_BAN_APFA:
		regs[1] = otp_read_one_bit(OTP_BAN_APFA_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_JTAG_LOCK:
		regs[1] = otp_read_one_bit(OTP_JTAG_SOFT_LOCK_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_PREORDER:
		regs[1] = otp_read_one_word(OTP_CUSTOM_KEY_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = ((reg_data & 0xFF0000) >> 16);
		break;

	case CMD_R_MODEL_ID:
		regs[1] = otp_read_one_word(OTP_MODEL_ID_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_COMMERCIAL:
		regs[1] = otp_read_one_bit(OTP_COMMERCIAL_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_TEST:
		regs[1] = otp_read_one_bit(OTP_TEST_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_WARRANTY:
		regs[1] = otp_read_one_bit(OTP_WARRANTY_ADDR, &reg_data);
		if (regs[1] == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	case CMD_R_NANTIRBK0_1:
		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR3, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[2] = reg_data;

		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR2, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[3] = reg_data;
		break;

	case CMD_R_NANTIRBK0_0:
		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR1, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[2] = reg_data;

		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR0, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[3] = reg_data;
		break;

	case CMD_R_NANTIRBK1_1:
		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR6, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[2] = reg_data;

		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR5, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[3] = reg_data;
		break;

	case CMD_R_NANTIRBK1_0:
		regs[1] = otp_read_one_word(OTP_ANTIRBK_ADDR4, &reg_data);
		if (regs[1] != RV_SUCCESS)
			break;
		regs[2] = reg_data;
		break;

	case CMD_R_ETC:
		if (regs[3] > 31) {
			regs[1] = RV_OTP_INVALID_BIT_INDEX;
			break;
		}
		rv = otp_read_one_bit(OTP_CUSTOM_ETC_ADDR + regs[3], &reg_data);
		regs[1] = rv;
		if (rv == RV_SUCCESS)
			regs[2] = reg_data;
		break;

	default:
		regs[1] = RV_OTP_NO_COMMAND;
	}
	return 0;
}
