/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2021. All rights reserved. * Description:OTP driver in register level. * Author: Hisilicon * Create: 2019/06/22 */ #include #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; }