/* SPDX-License-Identifier: GPL-2.0+
 *
 * Copyright 2020 Broadcom Ltd.
 */
/**********************************************************************
 *	
 * sko_hw.c			 
 * Includes both secure and non-secure otp operations 
 *
 *	
 *********************************************************************	
 */

#include <common.h>
#include <linux/types.h>
#include <linux/io.h>
#include "linux/printk.h"
#include <asm/arch/misc.h>
#include "otp_map_cmn.h"
#include "skp.h"

//#define DBG_OTP

#ifdef DBG_OTP
#define _ETR(__p__)  printf("%s \n\t\t\t\t -> :%s :%d  code %d / 0x%x\n",__FILE__,__FUNCTION__,__LINE__,__p__,__p__)
#define _DPRT(...)  printf(__VA_ARGS__) 
#else
#define _ETR(__p__)
#define _DPRT(...)
#endif

static otp_hw_cmn_err_t  lookup_row(otp_hw_cmn_t* dev, 
	u32 addr,
	otp_hw_cmn_row_t** row)
{
	u32 i;
	otp_hw_cmn_row_t* rows = dev->rows;
	for (i = 0; i < dev->row_max; i++) {
		if (rows[i].addr == addr) {
			*row = &rows[i];
			return OTP_HW_CMN_OK;	
		}
	}
	_ETR(OTP_HW_CMN_ERR_UNSP);
	return OTP_HW_CMN_ERR_UNSP;
}

/******************** FSR ACCESS FUNCTIONS ********************/
__weak u32 fsr_hw_readl(void* dev, u32 offs)
{
	u32 val;
	otp_hw_cmn_t* hw_dev = (otp_hw_cmn_t*)dev; 
	val = readl(hw_dev->mm+FSR_OFFSET+offs);
	_DPRT("0x%p > 0x%x\n",(void*)(hw_dev->mm+offs+FSR_OFFSET), val);
	return val;
}

__weak void fsr_hw_writel(void* dev, u32 offs, u32 data)
{
	otp_hw_cmn_t* hw_dev = (otp_hw_cmn_t*)dev; 
	_DPRT("0x%p < 0x%x\n",(void*)(hw_dev->mm+offs+FSR_OFFSET), data);
	writel(data, hw_dev->mm+FSR_OFFSET+offs);
}

static  otp_hw_cmn_err_t fsr_poll_otp_status(otp_hw_cmn_t* dev, 
						u32 fsr_offs)
{
	int to = 10000, errc = 0; 

	do {
		u32 st = fsr_hw_readl(dev, FSR_OTP_STATUS_OFFSET+fsr_offs);
		_DPRT("Fsr status 0x%x\n",st);
		if (!FSR_OTP_STATUS_ACTIVE(fsr_hw_readl(dev, FSR_OTP_STATUS_OFFSET+fsr_offs) )) {
			break;
		}
		udelay(1);
		to--;
	} while(to);

	errc = FSR_OTP_STATUS_CMD_ERROR(fsr_hw_readl(dev,FSR_OTP_STATUS_OFFSET+fsr_offs));
	if (errc) {
		_DPRT("%s: ERROR: otp status err 0x%x\n",
			__FUNCTION__, fsr_hw_readl(dev, FSR_OTP_STATUS_OFFSET+fsr_offs));
	}
	return (!to || errc);
}

static otp_hw_cmn_err_t fsr_read(otp_hw_cmn_t* dev,
			otp_hw_cmn_row_t  *row,
			otp_hw_cmn_row_conf_t* row_conf,
			u32 *data,
			u32 size)
{
        int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 fsr_offs;
	u32 raw_read = 0;
	u32 desc = 0;

	fsr_offs = FSR_SIZE*(row->addr-FSR_START_ROW_ADDR);
	if (row_conf && row_conf->op_type != OTP_HW_CMN_CTL_OTPCMD_ECC) {
		_DPRT("%s %d \n\t\t\t\t Raw read!\n",
			__FUNCTION__,__LINE__);
		raw_read = 1;
	}

	/* 1 - Check portal status */
	/* Issue RELOAD_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
	fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_RELOAD_DESC);
	/* Check cmd complete,FSR_PORTAL_FP0_OTP_STATUS active(31)=0 cmd_err(7:0)=0 */
	if (fsr_poll_otp_status(dev, fsr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}

	/* Check valid(20)==1, ready(21)==1 */
	if (!FSR_PORTAL_VALID(fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs))) {
		/* Portal is empty --> return zeroed data */
		_DPRT("%s %d \n\t\t\t\t Empty Portal! portal_status 0x%x\n",
			__FUNCTION__,__LINE__,
			fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs));
		memset(data, 0x0, size);
		rc = OTP_HW_CMN_OK; 
		goto err;
	}

	/* DESC status is in FSR_PORTAL_FP0_PORTAL_STATUS */
	/* RLOCK = 1 --> Cant read */
	if( FSR_PORTAL_RLOCK(fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs)) ) {
		rc = OTP_HW_CMN_ERR_FAIL; 
		_DPRT("%s %d \n\t\t\t\t Cant Read! RLOCK is set!\n",
			__FUNCTION__,__LINE__);
		_ETR(rc);
		goto err;
	}

	/* RAW and ECC_PGM_DIS = 0 --> Cant raw read */
	if( raw_read && !FSR_PORTAL_ECC_DIS(fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs))){
		rc = OTP_HW_CMN_ERR_FAIL; 
		_DPRT("%s %d \n\t\t\t\t Cant Read RAW! ECC_DIS is NOT set!\n",
			__FUNCTION__,__LINE__);
		_ETR(rc);
		goto err;
	}

	/* 2 - Read Decriptor to determine portal size */
	/* Issue READ_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
	fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_READ_DESC);
	/* Check command complete */
	if (fsr_poll_otp_status(dev, fsr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}
	/* DESC is in FSR_PORTAL_FP0_DATA */
	/* Check valid(31)==1 , this should succeed as PORTAL status is good*/
	desc = fsr_hw_readl(dev, FSR_DATA_OFFSET+fsr_offs);
	if(!FSR_RD_DGOOD(desc)) {
		_ETR(rc);
		_DPRT("%s %d \n\t\t\t\t desc 0x%x\n",
			__FUNCTION__,__LINE__,
			fsr_hw_readl(dev, FSR_DATA_OFFSET+fsr_offs));
		rc = OTP_HW_CMN_ERR_FAIL; 
		goto err;
	}
	/* Check if requested data fits inside this FSR, check SIZE(23:12) */
	if(size/sizeof(u32) > FSR_RD_DDATA_SIZE(desc)) {
		_ETR(rc);
		_DPRT("%s %d \n\t\t\t\t size 0x%x > 0x%0x\n",
			__FUNCTION__,__LINE__, size/sizeof(u32), FSR_RD_DDATA_SIZE(desc));
		rc =  OTP_HW_CMN_ERR_BAD_PARAM; 
		goto err;
	}

	/* 3 - Read data */
	for (i = 0; i < size/sizeof(u32); i++ ) {
		/* Issue READ_DATA command - Set WORD SEL(15:4) to pick word# */
		fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,
			(i<<FSR_PORTAL_OTP_CMD_WORD_SEL_SHIFT)|FSR_OTP_CMD_READ_DATA);
		/* Check command complete */
		if (fsr_poll_otp_status(dev, fsr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}

		/* Read data from FSR_PORTAL_FP0_DATA */
		data[i] = fsr_hw_readl(dev, FSR_DATA_OFFSET+fsr_offs);
		_DPRT("%s %d \n\t\t\t\t read data[%d] 0x%x \n",
			__FUNCTION__,__LINE__, i, data[i]);

		/* Optional - Read ECC from FSR_PORTAL_FP0_RD_ECC */
	}
	rc = OTP_HW_CMN_OK;
err:
	return rc;
}

static otp_hw_cmn_err_t fsr_write(otp_hw_cmn_t* dev,
			otp_hw_cmn_row_t  *row,
			otp_hw_cmn_row_conf_t* row_conf,
			u32 *data,
			u32 size)
{
        int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 fsr_offs; 
	otp_hw_cmn_ctl_cmd_t ctl = {.ctl = OTP_HW_CMN_CTL_OTPCMD_AUTH_PROG};
	u32 res = 0;
	u32 raw_write = 0;
	u32 desc = 0;
	otp_hw_cmn_t *ext_drv = dev->drv_ext;

	fsr_offs = FSR_SIZE*(row->addr-FSR_START_ROW_ADDR);
	if (row_conf && row_conf->op_type != OTP_HW_CMN_CTL_OTPCMD_ECC) {
		_DPRT("%s %d \n\t\t\t\t Raw write!\n",
			__FUNCTION__,__LINE__);
		raw_write = 1;
	}

	/* 1 - Place OTP in program mode */
	rc = ext_drv->ctl(ext_drv, &ctl, &res);
	if (rc) {
		_ETR(rc);
		goto err;
	}
	ctl.ctl = OTP_HW_CMN_CTL_OTPCMD_AUTH_PROG_DONE;
	rc = ext_drv->ctl(ext_drv, &ctl, &res);
	if (rc) {
		_ETR(rc);
		goto err;
	}

	/* 2 - Check portal status */
	/* Issue RELOAD_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
	fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_RELOAD_DESC);
	/* Check cmd complete,FSR_PORTAL_FP0_OTP_STATUS active(31)=0 cmd_err(7:0)=0 */
	if (fsr_poll_otp_status(dev, fsr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}

	/* Check valid(20)==1, ready(21)==1*/
	if (!FSR_PORTAL_VALID(fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs))) {
		/* If Portal DESC is not valid ---> empty!, program it */
		/* Set VALID = 1 and RLOCK = 0 */
		desc = FSR_RD_DESC_VALID_MASK<<FSR_RD_DESC_VALID_SHIFT;

		/* If RAW, set ECC_PGM_DIS */
		if( raw_write )
			desc |= FSR_RD_DESC_ECC_PGM_DIS_MASK<<FSR_RD_DESC_ECC_PGM_DIS_SHIFT;

		/* A - Write descriptor to FSR_PORTAL_FP0_DATA */
		fsr_hw_writel(dev, FSR_DATA_OFFSET+fsr_offs,desc);

		/* B - Issue PGM_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
		fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_PGM_DESC);
		/* Check cmd complete,FSR_PORTAL_FP0_OTP_STATUS active(31)=0 cmd_err(7:0)=0 */
		if (fsr_poll_otp_status(dev, fsr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}

		/* C - Issue PGM_DESC_ECC command to FSR_PORTAL_FP0_OTP_COMMAND */
		fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_PGM_DESC_ECC);
		/* Check cmd complete,FSR_PORTAL_FP0_OTP_STATUS active(31)=0 cmd_err(7:0)=0 */
		if (fsr_poll_otp_status(dev, fsr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}

		/* D - Issue RELOAD_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
		fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_RELOAD_DESC);
		/* Check cmd complete,FSR_PORTAL_FP0_OTP_STATUS active(31)=0 cmd_err(7:0)=0 */
		if (fsr_poll_otp_status(dev, fsr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}
	} else {
		/* Valid PORTAL and DESC */
		/* DESC status is in FSR_PORTAL_FP0_PORTAL_STATUS */
		/* RLOCK = 1 --> Cant write */
		if( FSR_PORTAL_RLOCK(fsr_hw_readl(dev, FSR_PORTAL_STATUS_OFFSET+fsr_offs)) ) {
			rc = OTP_HW_CMN_ERR_WRITE_FAIL; 
			_DPRT("%s %d \n\t\t\t\t Cant Write! RLOCK is set!\n",
				__FUNCTION__,__LINE__);
			_ETR(rc);
			goto err;
		}

		/* RAW and ECC_PGM_DIS = 0 --> Cant raw write */
		if( raw_write && !FSR_PORTAL_ECC_DIS(fsr_hw_readl(dev, 
				FSR_PORTAL_STATUS_OFFSET+fsr_offs))) {
			rc = OTP_HW_CMN_ERR_WRITE_FAIL; 
			_DPRT("%s %d \n\t\t\t\t Cant Write RAW! ECC_DIS is NOT set!\n",
				__FUNCTION__,__LINE__);
			_ETR(rc);
			goto err;
		}

		/* NOT RAW and ECC_PGM_DIS = 1 --> Cant non-raw write without ECC */
		if( !raw_write && FSR_PORTAL_ECC_DIS(fsr_hw_readl(dev, 
				FSR_PORTAL_STATUS_OFFSET+fsr_offs))) {
			rc = OTP_HW_CMN_ERR_WRITE_FAIL; 
			_DPRT("%s %d \n\t\t\t\t Cant Write Non-raw! ECC_DIS is set!\n",
				__FUNCTION__,__LINE__);
			_ETR(rc);
			goto err;
		}
	}


	/* 3 - Read Decriptor to determine portal size */
	/* Issue READ_DESC command to FSR_PORTAL_FP0_OTP_COMMAND */
	fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,FSR_OTP_CMD_READ_DESC);
	/* Check command complete */
	if (fsr_poll_otp_status(dev, fsr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}
	/* Read and check if descriptor is valid(31) */
	desc = fsr_hw_readl(dev, FSR_DATA_OFFSET+fsr_offs);
	if(!FSR_RD_DGOOD(desc)) {
		_ETR(rc);
		_DPRT("%s %d \n\t\t\t\t desc 0x%x\n",
			__FUNCTION__,__LINE__,
			fsr_hw_readl(dev, FSR_DATA_OFFSET+fsr_offs));
		rc = OTP_HW_CMN_ERR_FAIL; 
		goto err;
	}
	/* Check if requested data fits inside this FSR, check SIZE(23:12) */
	if(size/sizeof(u32) > FSR_RD_DDATA_SIZE(desc)) {
		_ETR(rc);
		_DPRT("%s %d \n\t\t\t\t size 0x%x > 0x%0x\n",
			__FUNCTION__,__LINE__, size/sizeof(u32), FSR_RD_DDATA_SIZE(desc));
		rc =  OTP_HW_CMN_ERR_BAD_PARAM; 
		goto err;
	}
	/* If not a raw_write, check if portal has been written to */
	if( !raw_write ) {
		for (i = 0; i < size/sizeof(u32); i++ ) {
			/* Issue READ_DATA command - Set WORD SEL(15:4) to pick word# */
			fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,
				(i<<FSR_PORTAL_OTP_CMD_WORD_SEL_SHIFT)|FSR_OTP_CMD_READ_DATA);
			/* Check command complete */
			if (fsr_poll_otp_status(dev, fsr_offs)) {
				rc = OTP_HW_CMN_ERR_TMO; 
				_ETR(rc);
				goto err;
			}

			/* Read ECC from FSR_PORTAL_FP0_RD_ECC */
			if(fsr_hw_readl(dev,FSR_RD_ECC_OFFSET+fsr_offs) & FSR_PORTAL_RD_ECC_MASK) {
				rc = OTP_HW_CMN_ERR_WRITE_FAIL; 
				_DPRT("%s %d \n\t\t\t\t Cannot overwrite portal! ECC is set!\n",
					__FUNCTION__,__LINE__);
				_ETR(rc);
				goto err;
			}
		}
	}

	/* 4 - Write data and ECC ( if reqd ) */
	for (i = 0; i < size/sizeof(u32); i++ ) {
		/* Read data from FSR_PORTAL_FP0_DATA */
		_DPRT("%s %d \n\t\t\t\t write data[%d] 0x%x \n",
			__FUNCTION__,__LINE__, i, data[i]);

		fsr_hw_writel(dev, FSR_DATA_OFFSET+fsr_offs, data[i]);

		/* Issue WRITE_DATA command - Set WORD SEL(15:4) to pick word# */
		fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,
			(i<<FSR_PORTAL_OTP_CMD_WORD_SEL_SHIFT)|FSR_OTP_CMD_PGM_DATA);
		/* Check command complete */
		if (fsr_poll_otp_status(dev, fsr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}

		if( !raw_write ) {		
			/* Issue WRITE_ECC command - Set WORD SEL(15:4) to pick word# */
			fsr_hw_writel(dev, FSR_OTP_CMD_OFFSET+fsr_offs,
				(i<<FSR_PORTAL_OTP_CMD_WORD_SEL_SHIFT)|FSR_OTP_CMD_PGM_DATA_ECC);
			/* Check command complete */
			if (fsr_poll_otp_status(dev, fsr_offs)) {
				rc = OTP_HW_CMN_ERR_TMO; 
				_ETR(rc);
				goto err;
			}
		}
	}
	
	_DPRT("Fused FSR rows as expected \n");
       	rc = OTP_HW_CMN_OK; 
err:
        return rc;
}

/******************** KSR ACCESS FUNCTIONS ********************/
__weak u32 ksr_hw_readl(void* dev, u32 offs)
{
	otp_hw_cmn_t* hw_dev = (otp_hw_cmn_t*)dev; 
	return readl(hw_dev->mm+KSR_OFFSET+offs);
}

__weak void ksr_hw_writel(void* dev, u32 offs, u32 data)
{
	otp_hw_cmn_t* hw_dev = (otp_hw_cmn_t*)dev; 
	_DPRT("0x%x < 0x%x\n",hw_dev->mm+offs, data);
	writel(data, hw_dev->mm+KSR_OFFSET+offs);
}

static  otp_hw_cmn_err_t ksr_poll_otp_status(otp_hw_cmn_t* dev, 
						u32 ksr_offs)
{
	int to = 10000, errc = 0; 

	do {
		u32 st = ksr_hw_readl(dev, KSR_OTP_STATUS_OFFSET+ksr_offs);
		_DPRT("Ksr status 0x%x\n",st);
		if (!KSR_OTP_STATUS_ACTIVE(ksr_hw_readl(dev, KSR_OTP_STATUS_OFFSET+ksr_offs) )) {
			break;
		}
		udelay(1);
		to--;
	} while(to);

	errc = KSR_OTP_STATUS_CMD_ERROR(ksr_hw_readl(dev,KSR_OTP_STATUS_OFFSET+ksr_offs));
	if (errc) {
		printf("%s: ERROR: otp status err 0x%x\n",
			__FUNCTION__, ksr_hw_readl(dev, KSR_OTP_STATUS_OFFSET+ksr_offs));
	}
	return (!to || errc);
}

static otp_hw_cmn_err_t ksr_read(otp_hw_cmn_t* dev,
			otp_hw_cmn_row_t  *row,
			otp_hw_cmn_row_conf_t* row_conf,
			u32 *data,
			u32 size)
{
        int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 ksr_offs;
	ksr_offs = KSR_SIZE*(row->addr-KSR_START_ROW_ADDR);
	if ( !KSR_PORTAL_VALID(ksr_hw_readl(dev, KSR_PORTAL_STATUS_OFFSET+ksr_offs)) ||
		!KSR_RD_DGOOD(ksr_hw_readl(dev, KSR_RD_DESC_OFFSET+ksr_offs))) {
		_ETR(rc);
		_DPRT("%s %d \n\t\t\t\t portal_status 0x%x rd_desc 0x%x\n",
			__FUNCTION__,__LINE__,ksr_hw_readl(dev, KSR_PORTAL_STATUS_OFFSET+ksr_offs), ksr_hw_readl(dev, KSR_RD_DESC_OFFSET+ksr_offs));
		rc = OTP_HW_CMN_ERR_KEY_EMPTY; 
		goto err;
	}
	for (i = 0; i <  size/sizeof(u32); i++ ) {
		data[i] = ksr_hw_readl(dev, KSR_DATA_OFFSET+i*sizeof(u32)+ksr_offs);
		_DPRT("%s %d \n\t\t\t\t data[%d] 0x%x \n",
			__FUNCTION__,__LINE__, i, data[i]);
	}
	rc = OTP_HW_CMN_OK;
err:
	return rc;

}

static otp_hw_cmn_err_t ksr_write(otp_hw_cmn_t* dev,
			otp_hw_cmn_row_t  *row,
			otp_hw_cmn_row_conf_t* row_conf,
			u32 *data,
			u32 size)
{
        int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 ksr_offs; 
	otp_hw_cmn_ctl_cmd_t ctl = {.ctl = OTP_HW_CMN_CTL_OTPCMD_AUTH_PROG};
	u32 res = 0;
	otp_hw_cmn_t *ext_drv = dev->drv_ext;
	ksr_offs = KSR_SIZE*(row->addr-KSR_START_ROW_ADDR);
	
/*
	1) Set OTP in program mode using classic CPU access as mentioned in Sec3.
	2) Write bit[31] (valid) to “1” and bit[7:0] PAC fields as required (refer RDB for definition) into KEY_PORTAL_KP0_PGM_DESCRIPTOR
	3) Issue PGM_DESCRIPTOR_WORD command, write bit[3:0] CMD field to “0x8”  in KEY_PORTAL_KP0_OTP_COMMAND, 
		this command programs into the OTP Macro one 32-bit Descriptor word. The data to be written comes from the KP_PGM_DESCRIPTOR register.
	4) Wait/Check for command_active to be low which indicates that the command issued is completed. 
		Poll for the register bit “command_active(bit [31])” in KEY_PORTAL_KP0_OTP_STATUS. 
		Once command is completed. Check for the bits[7:0] CMD_ERR (refer RDB for definition of the bits).
	5) Issue PGM_DESCRIPTOR_ECC command, write bit[3:0] CMD field to “0x9”  in KEY_PORTAL_KP0_OTP_COMMAND, 
		this command programs into the OTP Macro just the 10 ECC bits for the Descriptor row, 
		which the OTP Controller automatically calculates based on the OTP data already programmed in this row.
	6) Wait/Check for command_active to be low which indicates that the command issued is completed. 
		Poll for the register bit “command_active(bit [31])” in KEY_PORTAL_KP0_OTP_STATUS.
		Once command is completed. Check for the bits[7:0] CMD_ERR (refer RDB for definition of the bits).
	7) Write all the 8 Keys into KEY_PORTAL_KP0_DATA_0, KEY_PORTAL_KP0_DATA_1, KEY_PORTAL_KP0_DATA_2, KEY_PORTAL_KP0_DATA_3, 
		KEY_PORTAL_KP0_DATA_4, KEY_PORTAL_KP0_DATA_5, KEY_PORTAL_KP0_DATA_6, KEY_PORTAL_KP0_DATA_7 respectively.
	8) Issue PGM_KEY command, write bit[3:0] CMD field to “0x2”  in KEY_PORTAL_KP0_OTP_COMMAND, 
		this command programs (in the OTP Array) the entire key (including the CRC value) for this Key Portal. 
		The hardware internally calculates the CRC value for the key and programs it into the CRC OTP field for this key.
	9) Wait/Check for command_active to be low which indicates that the command issued is completed. 
		Poll for the register bit “command_active(bit [31])” in KEY_PORTAL_KP0_OTP_STATUS. 
		Once command is completed. Check for the bits[7:0] CMD_ERR (refer RDB for definition of the bits).
	10) Issue chip reset.
 
4.3 KSR PORTAL CPU READ ACCESS:

KSR sectioned region thru portal can be accessed only in secure-boot mode, i.e. bit 2 in Row13 and bit 28 in Row 14 should be “1”.

All 6 Key portals can be accessed in any order. Below sequence is explained by considering Key portal0 as an example same applies for other 5 Key portals as well.

	1) Upon chip reboot, design loads the KSR descriptor, checks for its validity, 
		and loads the keys into the registers once they are good.
	2) Read KEY_PORTAL_KP0_RD_DESCRIPTOR, check for bit[29] DGOOD, this should be “1” which indicates descriptor 
			validity checks passed and the also all 8 Keys are good with no CRC/ECC errors. 
		a. If DGOOD is “0”, all 8 keys read in Step(3) will be zeroes, KEY_PORTAL_KP0_PORTAL_STATUS 
			register will have more status info bits like Descriptor validity, Desc ECC error, Data ECC error, Keys loaded (Refer RDB).
	3) Read KEY_PORTAL_KP0_PAC register, this register initially gets loaded with the PAC values 
			from OTP but later can also be programmed, based on the PAC permissions Key portal registers can be accessed. Please refer RDB for more information. 
	4) Read all the 8 Keys which are loaded in KEY_PORTAL_KP0_DATA_0, KEY_PORTAL_KP0_DATA_1, KEY_PORTAL_KP0_DATA_2, KEY_PORTAL_KP0_DATA_3, KEY_PORTAL_KP0_DATA_4, KEY_PORTAL_KP0_DATA_5, KEY_PORTAL_KP0_DATA_6, KEY_PORTAL_KP0_DATA_7 respectively.

*/
	rc = ext_drv->ctl(ext_drv, &ctl, &res);
	if (rc) {
		_ETR(rc);
		goto err;
	}

	ctl.ctl = OTP_HW_CMN_CTL_OTPCMD_AUTH_PROG_DONE;
	rc = ext_drv->ctl(ext_drv, &ctl, &res);
	if (rc) {
		_ETR(rc);
		goto err;
	}

	_ETR((u32)row_conf->perm);
	_ETR(KSR_PORTAL_PGM_DESC_SRW_PERM);
	if (ksr_poll_otp_status(dev, ksr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}
	ksr_hw_writel(dev, KSR_PGM_DESC_OFFSET + ksr_offs, (u32)row_conf->perm);
	_ETR(ksr_hw_readl(dev, KSR_PGM_DESC_OFFSET+ksr_offs));
	ksr_hw_writel(dev, KSR_OTP_CMD_OFFSET + ksr_offs, KSR_PORTAL_OTP_CMD_PGM_DESC_WORD);
	if (ksr_poll_otp_status(dev, ksr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO; 
		_ETR(rc);
		goto err;
	}
	_ETR(KSR_PORTAL_OTP_CMD_PGM_DESC_WORD);
	if ((row_conf->op_type&OTP_HW_CMN_CTL_OTPCMD_ECC)) {
		ksr_hw_writel(dev, KSR_OTP_CMD_OFFSET+ksr_offs,KSR_PORTAL_OTP_CMD_PGM_DESC_ECC);
		if (ksr_poll_otp_status(dev,ksr_offs)) {
			rc = OTP_HW_CMN_ERR_TMO; 
			_ETR(rc);
			goto err;
		}
		_ETR(KSR_PORTAL_OTP_CMD_PGM_DESC_ECC);
	}
	for (i = 0 ; i <  size/sizeof(u32); i++) {
		ksr_hw_writel(dev, KSR_DATA_OFFSET+ksr_offs+i*sizeof(u32), data[i]);
	}
	ksr_hw_writel(dev, KSR_OTP_CMD_OFFSET + ksr_offs, KSR_PORTAL_OTP_CMD_PGM_KEY);
	_ETR(ksr_hw_readl(dev, KSR_OTP_CMD_OFFSET+ksr_offs));
	if (ksr_poll_otp_status(dev, ksr_offs)) {
		rc = OTP_HW_CMN_ERR_TMO;
		_ETR(rc);
		goto err;
	}
	
	_DPRT("Fused KSR rows as expected \n");
       	rc = OTP_HW_CMN_OK; 
err:
        return rc;
}

static otp_hw_cmn_err_t ksr_set_populated_pacs(otp_hw_cmn_t *dev, u32 pac_val)
{
	int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	/* First 2 KSR Portals have their PACS fused via their descriptors -- ignore */
	for (i = 2; i < KSR_MAX_ROW_ADDR; i++) { 
		/* If portal is valid and descriptor is good, we have valid data so lock NS */
		if ( KSR_PORTAL_VALID(ksr_hw_readl(dev, KSR_PORTAL_STATUS_OFFSET+i*KSR_SIZE)) &&
			KSR_RD_DGOOD(ksr_hw_readl(dev, KSR_RD_DESC_OFFSET+i*KSR_SIZE))) {
				ksr_hw_writel(dev, KSR_PAC_OFFSET+i*KSR_SIZE, pac_val);
		}
	}
	rc = OTP_HW_CMN_OK;
	return rc;
}

static otp_hw_cmn_err_t ksr_set_all_pacs(otp_hw_cmn_t *dev, u32 pac_val)
{
	int i;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	/* First 2 KSR Portals have their PACS fused via their descriptors -- ignore */
	for (i = 2; i < KSR_MAX_ROW_ADDR; i++) { 
		ksr_hw_writel(dev, KSR_PAC_OFFSET+i*KSR_SIZE, pac_val);
	}
	rc = OTP_HW_CMN_OK;
	return rc;

}

static otp_hw_cmn_err_t ksr_portal_lock(otp_hw_cmn_t *dev,
			u32 addr, 
			otp_hw_cmn_perm_t perm,
			u8 lock)
{
	otp_hw_cmn_row_t  *row;
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 curr_perm_mask = 0;
	u32 perm_mask = 0;
	if ( lookup_row(dev, addr, &row)) {
		rc = OTP_HW_CMN_ERR_UNSP;
		_ETR(rc); 
		goto err;
	}
	if (perm & OTP_HW_CMN_CTL_LOCK_PAC_NSRD) {
		perm_mask |= KSR_PERM_PAC_NSR_LOCK;
	}
   	if (perm & OTP_HW_CMN_CTL_LOCK_PAC_SRD) {
		perm_mask |= KSR_PERM_PAC_SR_LOCK;
	}
	if (perm & OTP_HW_CMN_CTL_LOCK_PAC_NSW) {
		perm_mask |= KSR_PERM_PAC_NSW_LOCK;
	}
	if (perm & OTP_HW_CMN_CTL_LOCK_PAC_SW) {
		perm_mask |= KSR_PERM_PAC_SW_LOCK;
	}
	if (perm & OTP_HW_CMN_CTL_LOCK_SRD) {
		perm_mask |= KSR_PERM_BLK_SR_LOCK;
	}
   	if (perm & OTP_HW_CMN_CTL_LOCK_NSRD) {
		perm_mask |= KSR_PERM_BLK_NSR_LOCK;
	}
   	if (perm & OTP_HW_CMN_CTL_LOCK_SW) {
		perm_mask |= KSR_PERM_BLK_SW_LOCK;
	}
	if (perm & OTP_HW_CMN_CTL_LOCK_NSW) {
		perm_mask |= KSR_PERM_BLK_NSW_LOCK;
	}
	if (perm_mask) {
		curr_perm_mask = ksr_hw_readl(dev, KSR_PAC_OFFSET + row->addr*KSR_SIZE);
		if( lock ) {
			ksr_hw_writel(dev, KSR_PAC_OFFSET + row->addr*KSR_SIZE, 
				curr_perm_mask|perm_mask);
		} else {
			ksr_hw_writel(dev, KSR_PAC_OFFSET + row->addr*KSR_SIZE, 
				curr_perm_mask&(~perm_mask));
		}

		rc = OTP_HW_CMN_OK;
	}
err:
	return rc;
}

static otp_hw_cmn_err_t ksr_multi_portal_lock(otp_hw_cmn_t *dev,
			otp_hw_cmn_perm_t perm,
			u8 lock)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	if( lock ) {
		if (perm & OTP_HW_CMN_CTL_LOCK_NS) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_LOCK_NS);
		} else if (perm & OTP_HW_CMN_CTL_LOCK_ALL) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_LOCK_ALL);
		} else if (perm & OTP_HW_CMN_CTL_LOCK_S) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_LOCK_S);
		} 
	} else {
		if (perm & OTP_HW_CMN_CTL_LOCK_NS || perm & OTP_HW_CMN_CTL_LOCK_NS_PROV ) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_UNLOCK_NS);
			if( perm & OTP_HW_CMN_CTL_LOCK_NS_PROV) {
				/* LOCK NS access for keys that are populated */
				rc = ksr_set_populated_pacs(dev, KSR_PERM_LOCK_NS);
			}
		} else if (perm & OTP_HW_CMN_CTL_LOCK_S) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_UNLOCK_S);
		} else if (perm & OTP_HW_CMN_CTL_LOCK_ALL) {
	  		rc = ksr_set_all_pacs(dev, KSR_PERM_UNLOCK_ALL);
		} 
	}
	return rc;
}

static otp_hw_cmn_err_t ksr_portal_status(otp_hw_cmn_t *dev,
			u32 addr,
			otp_hw_cmn_status_t status,
			u32* res)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	u32 perm_mask = 0,ksr_offs;
        otp_hw_cmn_row_t  *row = NULL;

	if ( lookup_row(dev, addr, &row)) {
		rc = OTP_HW_CMN_ERR_UNSP;
		_ETR(rc); 
		goto err;
	} 

	ksr_offs = KSR_SIZE*(row->addr-KSR_START_ROW_ADDR);
	perm_mask = ksr_hw_readl(dev, KSR_PAC_OFFSET+ksr_offs);
	
	if (!(perm_mask & KSR_PERM_PAC_NSR_LOCK)) {
   		status &= ~OTP_HW_CMN_STATUS_NSRD_PAC_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_PAC_SR_LOCK)) {
   		status &= ~OTP_HW_CMN_STATUS_SRD_PAC_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_PAC_NSW_LOCK)) {
		status &= ~OTP_HW_CMN_STATUS_NSW_PAC_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_PAC_SW_LOCK)) {
		status &= ~OTP_HW_CMN_STATUS_SW_PAC_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_BLK_SR_LOCK)) {
		status &= ~OTP_HW_CMN_STATUS_SRD_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_BLK_NSR_LOCK)) {
   		status &=  ~OTP_HW_CMN_STATUS_NSRD_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_BLK_SW_LOCK)) {
   		status &= ~OTP_HW_CMN_STATUS_SW_LOCKED;
	}
	if (!(perm_mask & KSR_PERM_BLK_NSW_LOCK)) {
		status &= ~OTP_HW_CMN_STATUS_NSW_LOCKED;
	}
	if ( !KSR_PORTAL_VALID(ksr_hw_readl(dev, KSR_PORTAL_STATUS_OFFSET+ksr_offs)) ||
		!KSR_RD_DGOOD(ksr_hw_readl(dev, KSR_RD_DESC_OFFSET+ksr_offs))) {
		status &= ~OTP_HW_CMN_STATUS_ROW_DATA_VALID;
	}
	*res = status;
	rc = OTP_HW_CMN_OK;
err:
	return rc;
}


/******************** SKP ACCESS FUNCTIONS ********************/
static  otp_hw_cmn_err_t skp_write(otp_hw_cmn_t* dev, 
		u32 addr,
		otp_hw_cmn_row_conf_t* conf,
		const u32* data,
		u32 size)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
        otp_hw_cmn_row_t *row;
	otp_hw_cmn_row_conf_t* row_conf;
	if (!dev->drv_ext) {
		rc = OTP_HW_CMN_ERR_INVAL;
		_ETR(rc);
		goto err;
	}
	if (lookup_row(dev, addr, &row)) {
		rc = OTP_HW_CMN_ERR_UNSP;
		_ETR(rc);
		goto err;
	}

	if ((int)size <= 0 || (int)size > row->range*sizeof(u32) || (int)row->addr < 0) {
		rc = OTP_HW_CMN_ERR_INVAL; 
		_ETR(rc);
		goto err;
	}

	row_conf = conf?conf:&row->conf;
	if( (int)row->addr < KSR_MAX_ROW_ADDR ) {
		rc = ksr_write( dev, row, row_conf, (u32*)data, size );
	} else if(((int)row->addr >=FSR_START_ROW_ADDR) && ((int)row->addr < FSR_MAX_ROW_ADDR)) {
		rc = fsr_write( dev, row, row_conf, (u32*)data, size );
	} else {
		rc = OTP_HW_CMN_ERR_INVAL; 
		_ETR(rc);
	}
err:
	_DPRT("%s: rc %d\n", __FUNCTION__, rc);
	return rc;

}

static otp_hw_cmn_err_t skp_read(otp_hw_cmn_t *dev,
			u32 addr,
			otp_hw_cmn_row_conf_t* conf,
			u32 *data,
			u32 size)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
        otp_hw_cmn_row_t  *row;
	otp_hw_cmn_row_conf_t* row_conf;
	if (!dev->drv_ext) {
		rc = OTP_HW_CMN_ERR_INVAL; 
		_ETR(rc);
		goto err;
	}
	if( lookup_row(dev, addr, &row)) {
		rc = OTP_HW_CMN_ERR_UNSP;
		_ETR(rc); 
		goto err;
	}
	if ((int)size <= 0 || (int)size > row->range*sizeof(u32) || (int)row->addr < 0) {
		rc = OTP_HW_CMN_ERR_INVAL; 
		_ETR(rc);
		goto err;
	}

	row_conf = conf? conf : &row->conf;
	if( (int)row->addr < KSR_MAX_ROW_ADDR ) {
		rc = ksr_read( dev, row, row_conf, (u32*)data, size );
	} else if(((int)row->addr >=FSR_START_ROW_ADDR) && ((int)row->addr < FSR_MAX_ROW_ADDR)) {
		rc = fsr_read( dev, row, row_conf, (u32*)data, size );
	} else {
		rc = OTP_HW_CMN_ERR_INVAL; 
		_ETR(rc);
	}
err:
	_DPRT("%s: rc %d\n", __FUNCTION__, rc);
	return rc;
}

static  otp_hw_cmn_err_t skp_hw_write(otp_hw_cmn_t* dev, 
		u32 addr,
		const u32* data,
		u32 size)
{
	otp_hw_cmn_row_conf_t *cfg =  
				!(dev->ctl_cmd.ctl&OTP_HW_CMN_CTL_CONF)? NULL: &dev->row_conf;
	_ETR(dev->ctl_cmd.ctl);	
	return skp_write(dev, addr, cfg, data, size);
}

static otp_hw_cmn_err_t skp_hw_read(otp_hw_cmn_t *dev,
			u32 addr,
			u32 *data,
			u32 size)
{

	otp_hw_cmn_row_conf_t *cfg =  
			  !(dev->ctl_cmd.ctl&OTP_HW_CMN_CTL_CONF)? NULL : &dev->row_conf;
	_ETR(dev->ctl_cmd.ctl);	
	return skp_read(dev, addr, cfg, data, size);

}

static otp_hw_cmn_err_t skp_hw_lock(otp_hw_cmn_t *dev,
			u32 addr, 
			otp_hw_cmn_perm_t perm,
			u8 lock)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	switch ( perm ) {
		case OTP_HW_CMN_CTL_LOCK_NS:
		case OTP_HW_CMN_CTL_LOCK_NS_PROV:
		case OTP_HW_CMN_CTL_LOCK_ALL:
		case OTP_HW_CMN_CTL_LOCK_S:
			/* TODO: Add FSR support */
			rc = ksr_multi_portal_lock(dev, perm, lock);
		break;
		default:
			/* TODO: Add FSR support */
			rc = ksr_portal_lock(dev, addr, perm, lock);
	}
	return rc;
}


static otp_hw_cmn_err_t skp_hw_status(otp_hw_cmn_t *dev,
			u32 addr,
			otp_hw_cmn_status_t status,
			u32* res)
{

	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_FAIL; 
	/* TODO: Add FSR support */
	rc = ksr_portal_status(dev, addr, status, res);
	return rc;
}

__weak otp_hw_cmn_err_t sotp_hw_cmn_ctl(otp_hw_cmn_t *dev, 
			const otp_hw_cmn_ctl_cmd_t *cmd,
			u32* res)
{
	otp_hw_cmn_err_t rc = OTP_HW_CMN_ERR_UNSP;
	switch ((u32)(cmd->ctl)) {
   		case OTP_HW_CMN_CTL_CONF:
			memcpy(&dev->row_conf, 
				(otp_hw_cmn_row_conf_t*)cmd->data, 
				cmd->size);

			dev->ctl_cmd.ctl |= cmd->ctl;
			rc = OTP_HW_CMN_OK;
			break;
   		case (~((u32)OTP_HW_CMN_CTL_CONF)):
			dev->ctl_cmd.ctl &= ~cmd->ctl;
			rc = OTP_HW_CMN_OK;
			break;
   		case OTP_HW_CMN_CTL_UNLOCK: {
				otp_hw_ctl_data_t* ctl_data = (otp_hw_ctl_data_t*)cmd->data;
				rc = skp_hw_lock(dev, ctl_data->addr, ctl_data->perm, 0);
			}
			break;
		case OTP_HW_CMN_CTL_LOCK: {
				otp_hw_ctl_data_t* ctl_data = (otp_hw_ctl_data_t*)cmd->data;
				rc = skp_hw_lock(dev, ctl_data->addr, ctl_data->perm, 1);
			}
			break;
   		case OTP_HW_CMN_CTL_STATUS: {
				otp_hw_ctl_data_t* ctl_data = (otp_hw_ctl_data_t*)cmd->data;	
				rc = skp_hw_status(dev, ctl_data->addr, ctl_data->status, res);
			}
			break;
		default :
			break;	
	}
	return rc;
	
}


__weak otp_hw_cmn_err_t sotp_hw_dev_mmap(otp_hw_cmn_t* dev)
{
	dev->mm = KSR_BASE;
	return OTP_HW_CMN_OK;
}

__weak otp_hw_cmn_err_t sotp_hw_init(otp_hw_cmn_t* dev)
{
	DEFINE_SOTP_MAP_ROW_INITLR(rows);
	if (sotp_hw_dev_mmap(dev)) {
		return OTP_HW_CMN_ERR_FAIL;
	}
	dev->rows = rows;
	dev->row_max = sizeof(rows)/sizeof(otp_hw_cmn_row_t);
	return OTP_HW_CMN_OK;
}


otp_hw_cmn_err_t sotp_hw_cmn_init(otp_hw_cmn_t* dev)
{
	if (sotp_hw_init(dev)) {
		_ETR(OTP_HW_CMN_ERR_FAIL);
		return OTP_HW_CMN_ERR_FAIL;
	}
	dev->read = skp_hw_read;
	dev->write = skp_hw_write;
	dev->ctl = sotp_hw_cmn_ctl;
	return OTP_HW_CMN_OK;
}
