You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
12 KiB
437 lines
12 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2021. All rights reserved.
|
|
* Description:OTP driver in register level.
|
|
* Author: Hisilicon
|
|
* Create: 2019/06/22
|
|
*/
|
|
#include <linux/mutex.h>
|
|
#include "osal_ext.h"
|
|
#include "drv_otp_define.h"
|
|
#include "drv_otp_proc.h"
|
|
#include "drv_ioctl_otp.h"
|
|
#include "drv_otp.h"
|
|
#include "hal_otp_reg.h"
|
|
#include "hal_otp.h"
|
|
|
|
#define LOOP_MAX 100000 /* Waiting for logic completion count. */
|
|
#define DELEY_TIME_CNT 1 /* Delay time, xxx us. */
|
|
#define TIME_LOOP 100
|
|
|
|
static DEFINE_MUTEX(otp_mutex);
|
|
|
|
/* static function */
|
|
static td_void _otp_write_reg(td_u32 addr, td_u32 val)
|
|
{
|
|
struct otp_mgmt *mgmt = priv_get_otp_mgmt();
|
|
|
|
reg_write((mgmt->io_base + addr), val);
|
|
ext_info_otp("W 0x%x 0x%x\n", mgmt->reg.reg + addr, val);
|
|
return;
|
|
}
|
|
|
|
static td_u32 _otp_read_reg(td_u32 addr)
|
|
{
|
|
td_u32 val = 0;
|
|
struct otp_mgmt *mgmt = priv_get_otp_mgmt();
|
|
|
|
reg_read((mgmt->io_base + addr), val);
|
|
ext_info_otp("R 0x%x 0x%x\n", mgmt->reg.reg + addr, val);
|
|
return val;
|
|
}
|
|
|
|
static td_s32 _otp_check_error_status(td_void)
|
|
{
|
|
otp_ctr_st0 st0;
|
|
|
|
st0.u32 = _otp_read_reg(OTP_CTR_ST0);
|
|
if (st0.bits.otp_init_rdy == 0) {
|
|
print_dbg_hex3(OTP_CTR_ST0, st0.u32, SOC_ERR_OTP_NOT_INITRDY);
|
|
return SOC_ERR_OTP_NOT_INITRDY;
|
|
}
|
|
if (st0.bits.err == 1) {
|
|
print_dbg_hex3(OTP_CTR_ST0, st0.u32, SOC_ERR_OTP_PROG_PERM);
|
|
return SOC_ERR_OTP_PROG_PERM;
|
|
}
|
|
if (st0.bits.prm_rd_fail == 1) {
|
|
print_dbg_hex3(OTP_CTR_ST0, st0.u32, SOC_ERR_OTP_FAIL_PRMRD);
|
|
return SOC_ERR_OTP_FAIL_PRMRD;
|
|
}
|
|
if (st0.bits.rd_fail == 1) {
|
|
print_dbg_hex3(OTP_CTR_ST0, st0.u32, SOC_ERR_OTP_FAIL_RD);
|
|
return SOC_ERR_OTP_FAIL_RD;
|
|
}
|
|
if (st0.bits.prog_disable == 1) {
|
|
print_dbg_hex3(OTP_CTR_ST0, st0.u32, SOC_ERR_OTP_DISABLE_PROG);
|
|
return SOC_ERR_OTP_DISABLE_PROG;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 _otp_wait_ctrl_idle(td_void)
|
|
{
|
|
otp_rw_ctrl reg;
|
|
td_u32 cnt = 0;
|
|
td_s32 type;
|
|
td_u32 start_status = 0;
|
|
td_u32 chip_status = 0;
|
|
otp_v100_ctrl_status reg_tmp;
|
|
|
|
type = otp_get_chip_type();
|
|
|
|
while ((chip_status == start_status) && (cnt++ < LOOP_MAX)) {
|
|
if (type == EXT_OLD_FW) {
|
|
start_status = 0;
|
|
reg_tmp.u32 = _otp_read_reg(OTP_V100_CTRL_STATUS);
|
|
chip_status = reg_tmp.bits.ctrl_ready;
|
|
} else {
|
|
reg.u32 = _otp_read_reg(OTP_RW_CTRL);
|
|
chip_status = reg.bits.start;
|
|
start_status = 1;
|
|
}
|
|
osal_udelay(DELEY_TIME_CNT);
|
|
|
|
if ((cnt % TIME_LOOP) == 0) {
|
|
osal_msleep(0);
|
|
}
|
|
}
|
|
if (cnt >= LOOP_MAX) {
|
|
return SOC_ERR_OTP_TIMEOUT;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_init(td_void)
|
|
{
|
|
td_s32 ret;
|
|
struct otp_mgmt *mgmt = priv_get_otp_mgmt();
|
|
td_u32 *reg = (td_u32 *)&mgmt->reg;
|
|
|
|
/* otp base register */
|
|
ret = osal_dts_get_array_byname("huanglong,perm", "reg", reg, sizeof(struct dts_reg) / sizeof(td_u32));
|
|
if (ret < 0) {
|
|
print_err_func(osal_dts_get_array_byname, ret);
|
|
return TD_FAILURE;
|
|
}
|
|
soc_print("permission address[%x-%x].\n", mgmt->reg.reg, mgmt->reg.range);
|
|
|
|
mgmt->io_base = osal_ioremap_nocache(mgmt->reg.reg, mgmt->reg.range);
|
|
if (mgmt->io_base == TD_NULL) {
|
|
print_err_hex3(mgmt->reg.reg, mgmt->reg.range, SOC_ERR_OTP_MEM_MAP);
|
|
return SOC_ERR_OTP_MEM_MAP;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_void hal_otp_deinit(td_void)
|
|
{
|
|
struct otp_mgmt *mgmt = priv_get_otp_mgmt();
|
|
|
|
osal_iounmap(mgmt->io_base, OSAL_IOUNMAP_SIZE);
|
|
return;
|
|
}
|
|
|
|
td_s32 hal_otp_reset(td_void)
|
|
{
|
|
otp_ctr_st0 otp_ctr_st0;
|
|
td_u32 cnt = 0;
|
|
td_s32 type;
|
|
|
|
type = otp_get_chip_type();
|
|
if (type == EXT_OLD_FW) {
|
|
ext_err_otp("Not support!\n");
|
|
return SOC_ERR_OTP_NOT_SUPPORT;
|
|
}
|
|
|
|
otp_func_enter();
|
|
_otp_write_reg(OTP_SH_UPDATE, 0x1); /* Shadow register update request.0:no request; 1:request */
|
|
|
|
otp_ctr_st0.u32 = _otp_read_reg(OTP_CTR_ST0);
|
|
while ((otp_ctr_st0.bits.soft_req_otp_rdy == TD_FALSE) && (cnt++ < LOOP_MAX)) {
|
|
otp_ctr_st0.u32 = _otp_read_reg(OTP_CTR_ST0);
|
|
osal_udelay(DELEY_TIME_CNT);
|
|
}
|
|
if (cnt >= LOOP_MAX) {
|
|
print_err_hex4(OTP_CTR_ST0, otp_ctr_st0.u32, cnt, SOC_ERR_OTP_TIMEOUT);
|
|
return SOC_ERR_OTP_TIMEOUT;
|
|
}
|
|
otp_func_exit();
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* solve ACPU/TCPU contention read/write OTP conflict problem
|
|
* reg_addr: register offset
|
|
* write_value: register value
|
|
* addr_same: check current addr address is same as the value of OTP_RADDR register. 0:diff; 1: same
|
|
* operation: 0: read; 1 write
|
|
*/
|
|
static td_s32 hal_otp_check(td_u32 reg_addr, td_u32 write_value, td_u32 addr_same, td_u32 operation)
|
|
{
|
|
td_u32 cnt = 0;
|
|
otp_v100_cpu_rw_ctrl cpurw_ctrl;
|
|
td_u32 value = write_value;
|
|
|
|
if (reg_addr == OTP_V100_CPU_RW_CTRL) {
|
|
cpurw_ctrl.u32 = value;
|
|
cpurw_ctrl.bits.wr_sel = operation;
|
|
cpurw_ctrl.bits.wr_enable = operation;
|
|
cpurw_ctrl.bits.rd_enable = 0x01;
|
|
cpurw_ctrl.bits.cpu_size = 0x01;
|
|
if (operation == 0x01 || addr_same == 1) {
|
|
cpurw_ctrl.bits.rd_enable = 0x00;
|
|
}
|
|
|
|
value = cpurw_ctrl.u32;
|
|
}
|
|
|
|
while (cnt < LOOP_MAX) {
|
|
cnt++;
|
|
_otp_write_reg(reg_addr, value);
|
|
|
|
if (_otp_read_reg(reg_addr) == value) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cnt >= LOOP_MAX) {
|
|
print_err_val(reg_addr);
|
|
return SOC_ERR_OTP_TIMEOUT;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_read(td_u32 addr, td_u32 *value)
|
|
{
|
|
td_s32 ret;
|
|
otp_rw_ctrl reg_ctr;
|
|
td_s32 type;
|
|
td_u32 read_addr, fake_addr;
|
|
td_u32 otp_addr, otp_cpurw_ctrl;
|
|
|
|
/* ****** proc virtualotp begin ******* */
|
|
ret = fake_otp_virtual_read(addr, value);
|
|
if (ret != SOC_ERR_OTP_NON_FAKE_MODE) {
|
|
return ret;
|
|
}
|
|
/* ****** proc virtualotp end ******* */
|
|
if (value == TD_NULL) {
|
|
print_err_code(SOC_ERR_OTP_PTR_NULL);
|
|
return SOC_ERR_OTP_PTR_NULL;
|
|
}
|
|
|
|
mutex_lock(&otp_mutex);
|
|
ret = _otp_wait_ctrl_idle();
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(_otp_wait_ctrl_idle, ret);
|
|
return ret;
|
|
}
|
|
|
|
type = otp_get_chip_type();
|
|
if (type == EXT_PANDA_FW) {
|
|
_otp_write_reg(OTP_RADDR, addr);
|
|
|
|
reg_ctr.bits.wr_sel = OTP_OPT_READ;
|
|
reg_ctr.bits.start = TD_TRUE;
|
|
_otp_write_reg(OTP_RW_CTRL, reg_ctr.u32);
|
|
read_addr = OTP_RDATA;
|
|
} else if (type == EXT_OLD_FW) {
|
|
otp_addr = _otp_read_reg(OTP_V100_RADDR);
|
|
otp_cpurw_ctrl = _otp_read_reg(OTP_V100_CPU_RW_CTRL);
|
|
|
|
if (otp_addr == addr) {
|
|
fake_addr = (addr == OTP_V100_FAKE_RADDR) ? OTP_V100_FAKE_RADDR_1 : OTP_V100_FAKE_RADDR;
|
|
ret |= hal_otp_check(OTP_V100_CPU_RW_CTRL, otp_cpurw_ctrl, 1, 0);
|
|
ret |= hal_otp_check(OTP_V100_RADDR, fake_addr, 1, 0);
|
|
}
|
|
ret |= hal_otp_check(OTP_V100_CPU_RW_CTRL, otp_cpurw_ctrl, 0, 0);
|
|
ret |= hal_otp_check(OTP_V100_RADDR, addr, 0, 0);
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(hal_otp_check, ret);
|
|
return ret;
|
|
}
|
|
|
|
read_addr = OTP_V100_RDATA;
|
|
}
|
|
|
|
ret = _otp_wait_ctrl_idle();
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(_otp_wait_ctrl_idle, ret);
|
|
return ret;
|
|
}
|
|
*value = _otp_read_reg(read_addr);
|
|
|
|
if (type == EXT_OLD_FW) {
|
|
mutex_unlock(&otp_mutex);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&otp_mutex);
|
|
return _otp_check_error_status();
|
|
}
|
|
|
|
td_s32 hal_otp_write(td_u32 addr, td_u32 value)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 i;
|
|
|
|
if (is_word_align(addr)) {
|
|
print_err_code(SOC_ERR_OTP_INVALID_PARA);
|
|
return SOC_ERR_OTP_INVALID_PARA;
|
|
}
|
|
for (i = 0; i < WORD_SIZE; i++) {
|
|
/* Only genarate 8 bits */
|
|
ret = hal_otp_write_byte(addr + i, (value >> (BYTE_WIDTH * i)) & 0xff);
|
|
if (ret != TD_SUCCESS) {
|
|
print_err_func_hex3(hal_otp_write_byte, addr + i, (value >> (BYTE_WIDTH * i)) & 0xff, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_read_byte(td_u32 addr, td_u8 *value)
|
|
{
|
|
td_s32 ret;
|
|
td_u32 v = 0;
|
|
|
|
if (value == TD_NULL) {
|
|
print_err_code(SOC_ERR_OTP_PTR_NULL);
|
|
return SOC_ERR_OTP_PTR_NULL;
|
|
}
|
|
ret = hal_otp_read(word_align(addr), &v);
|
|
if (ret != TD_SUCCESS) {
|
|
print_err_func_hex2(hal_otp_read, word_align(addr), ret);
|
|
return ret;
|
|
}
|
|
*value = byte_den(v, addr);
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_write_byte(td_u32 addr, td_u8 value)
|
|
{
|
|
td_s32 ret;
|
|
otp_rw_ctrl reg_ctr;
|
|
td_s32 type;
|
|
td_u32 otp_cpurw_ctrl;
|
|
|
|
/* ****** proc virtualotp begin ******* */
|
|
ret = fake_otp_virtual_write_byte(addr, value);
|
|
if (ret == TD_SUCCESS) {
|
|
return ret;
|
|
} else if (ret != SOC_ERR_OTP_NON_FAKE_MODE) {
|
|
print_err_func_hex3(fake_otp_virtual_write_byte, addr, value, ret);
|
|
return ret;
|
|
} else {
|
|
}
|
|
/* ****** proc virtualotp end ******* */
|
|
mutex_lock(&otp_mutex);
|
|
ret = _otp_wait_ctrl_idle();
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(_otp_wait_ctrl_idle, ret);
|
|
return ret;
|
|
}
|
|
|
|
type = otp_get_chip_type();
|
|
if (type == EXT_PANDA_FW) {
|
|
_otp_write_reg(OTP_WADDR, addr);
|
|
_otp_write_reg(OTP_WDATA, value);
|
|
|
|
_otp_write_reg(OTP_RW_CTRL, 0x5);
|
|
reg_ctr.bits.wr_sel = OTP_OPT_WRITE;
|
|
reg_ctr.bits.start = TD_TRUE;
|
|
_otp_write_reg(OTP_RW_CTRL, reg_ctr.u32);
|
|
} else if (type == EXT_OLD_FW) {
|
|
otp_cpurw_ctrl = _otp_read_reg(OTP_V100_CPU_RW_CTRL);
|
|
ret |= hal_otp_check(OTP_V100_CPU_RW_CTRL, otp_cpurw_ctrl, 0, 1);
|
|
ret |= hal_otp_check(OTP_V100_WADDR, addr, 0, 1);
|
|
ret |= hal_otp_check(OTP_V100_WDATA, value, 0, 1);
|
|
ret |= hal_otp_check(OTP_V100_WR_START, 0x1, 0, 1);
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(hal_otp_check, ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
}
|
|
|
|
ret = _otp_wait_ctrl_idle();
|
|
if (ret != TD_SUCCESS) {
|
|
mutex_unlock(&otp_mutex);
|
|
print_err_func(_otp_wait_ctrl_idle, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (type == EXT_OLD_FW) {
|
|
mutex_unlock(&otp_mutex);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&otp_mutex);
|
|
return _otp_check_error_status();
|
|
}
|
|
|
|
td_s32 hal_otp_write_bit(td_u32 addr, td_u32 bit_pos)
|
|
{
|
|
td_s32 ret;
|
|
td_u8 data;
|
|
|
|
if (bit_pos >= BYTE_WIDTH) {
|
|
print_err_hex2(bit_pos, SOC_ERR_OTP_INVALID_PARA);
|
|
return SOC_ERR_OTP_INVALID_PARA;
|
|
}
|
|
data = (1 << bit_pos);
|
|
ret = hal_otp_write_byte(addr, data);
|
|
if (ret != TD_SUCCESS) {
|
|
print_err_func_hex3(hal_otp_write_byte, addr, data, ret);
|
|
return ret;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_read_bits_onebyte(td_u32 addr, td_u32 start_bit, td_u32 bit_width, td_u8 *value)
|
|
{
|
|
td_s32 ret;
|
|
td_u8 data = 0;
|
|
|
|
if (value == TD_NULL) {
|
|
return SOC_ERR_OTP_PTR_NULL;
|
|
}
|
|
|
|
if (start_bit + bit_width > BYTE_WIDTH) {
|
|
print_err_hex3(start_bit, bit_width, SOC_ERR_OTP_INVALID_PARA);
|
|
return SOC_ERR_OTP_INVALID_PARA;
|
|
}
|
|
ret = hal_otp_read_byte(addr, &data);
|
|
if (ret != TD_SUCCESS) {
|
|
print_err_func_hex2(hal_otp_read_byte, addr, ret);
|
|
return ret;
|
|
}
|
|
data &= gen_mask(start_bit + bit_width - 1, start_bit);
|
|
*value = data >> start_bit;
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
td_s32 hal_otp_write_bits_onebyte(td_u32 addr, td_u32 start_bit, td_u32 bit_width, td_u8 value)
|
|
{
|
|
td_s32 ret;
|
|
td_u8 data;
|
|
|
|
if (start_bit + bit_width > BYTE_WIDTH) {
|
|
print_err_hex3(start_bit, bit_width, SOC_ERR_OTP_INVALID_PARA);
|
|
return SOC_ERR_OTP_INVALID_PARA;
|
|
}
|
|
data = (value << start_bit) & gen_mask(start_bit + bit_width - 1, start_bit);
|
|
ret = hal_otp_write_byte(addr, data);
|
|
if (ret != TD_SUCCESS) {
|
|
print_err_func_hex3(hal_otp_write_byte, addr, data, ret);
|
|
return ret;
|
|
}
|
|
return TD_SUCCESS;
|
|
}
|