/** NVT OTP This file will operate otp @file nvt-otp.c @ingroup @note Copyright Novatek Microelectronics Corp. 2018. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. */ #include #include #include #include #include #ifndef CHKPNT #define CHKPNT printk("\033[37mCHK: %d, %s\033[0m\r\n", __LINE__, __func__) #endif #ifndef DBGD #define DBGD(x) printk("\033[0;35m%s=%d\033[0m\r\n", #x, x) #endif #ifndef DBGH #define DBGH(x) printk("\033[0;35m%s=0x%08X\033[0m\r\n", #x, x) #endif #ifndef DBG_DUMP #define DBG_DUMP(fmtstr, args...) printk(fmtstr, ##args) #endif #ifndef DBG_ERR #define DBG_ERR(fmtstr, args...) printk("\033[0;31mERR:%s() \033[0m" fmtstr, __func__, ##args) #endif #ifndef DBG_WRN #define DBG_WRN(fmtstr, args...) printk("\033[0;33mWRN:%s() \033[0m" fmtstr, __func__, ##args) #endif #if 0 #define DBG_IND(fmtstr, args...) printk("%s(): " fmtstr, __func__, ##args) #else #ifndef DBG_IND #define DBG_IND(fmtstr, args...) #endif #endif struct mutex otp_lock; #define otp_loc() {mutex_lock(&otp_lock); } #define otp_unl() {mutex_unlock(&otp_lock); } extern INT32 efuse_read_addr(UINT32 efuse_addr, UINT32 address); static UINT32 OTP_READ[] = { 0xE92D41F0, 0xE1A04000, 0xE1A06001, 0xE30F2D66, 0xE3E00020, 0xE2867094, 0xE3A0800D, 0xE3A0501F, 0xE0045005, 0xE1550008, 0xC1A0F007, 0xE1A00802, 0xE286703C, 0xE3A01001, 0xE5801020, 0xE5901030, 0xE7E01051, 0xE3510001, 0x11A0F007, 0xE3A01000, 0xE0811004, 0xE3A05000, 0xE3A05000, 0xE2867070, 0xE5801004, 0xE5902020, 0xE3822002, 0xE5802020, 0xE5902020, 0xE7E020D2, 0xE3520001, 0x01A0F007, 0xE5902030, 0xE5802030, 0xE3A02000, 0xE5802020, 0xE5900008, 0xE8BD81F0, 0xE320F000, 0xEAFFFFFE, 0xE320F000, }; static UINT32 OTP_OP[] = { 0xE92D41F0, 0xE1A04000, 0xE1A06001, 0xE30F2D66, 0xE1A00802, 0xE30F2D62, 0xE1A03802, 0xE2867028, 0xE3A01001, 0xE5801020, 0xE5901030, 0xE7E01051, 0xE3510001, 0x11A0F007, 0xE3A01010, 0xE0811104, 0xE3A05001, 0xE5835020, 0xE286705C, 0xE5801004, 0xE5902020, 0xE3822002, 0xE5802020, 0xE5902020, 0xE7E020D2, 0xE3520001, 0x01A0F007, 0xE5902030, 0xE5802030, 0xE2811001, 0xE3A05002, 0xE5835020, 0xE2867094, 0xE5801004, 0xE5902020, 0xE3822002, 0xE5802020, 0xE5902020, 0xE7E020D2, 0xE3520001, 0x01A0F007, 0xE5902030, 0xE5802030, 0xE2811001, 0xE3A05004, 0xE5835020, 0xE28670CC, 0xE5801004, 0xE5902020, 0xE3822002, 0xE5802020, 0xE5902020, 0xE7E020D2, 0xE3520001, 0x01A0F007, 0xE5902030, 0xE5802030, 0xE2811001, 0xE3A05008, 0xE5835020, 0xE2867F41, 0xE5801004, 0xE5902020, 0xE3822002, 0xE5802020, 0xE5902020, 0xE7E020D2, 0xE3520001, 0x01A0F007, 0xE5902030, 0xE5802030, 0xE3A02000, 0xE5802020, 0xE8BD81F0, 0xE320F000, 0xEAFFFFFE, 0xE320F000, }; static UINT32 DMA_USAGE_DATA[] = { 0xE92D41F0, 0xE1A04000, 0xE1A06001, 0xE2868020, 0xE30F2D00, 0xE1A02802, 0xE1A03A04, 0xE3A07A09, 0xE1822003, 0xE1827007, 0xE5921054, 0xE5970000, 0xE1A05001, 0xE1A06000, 0xE3500000, 0x01A0F008, 0xE3A03064, 0xE7F91051, 0xE7F90050, 0xE0030193, 0xE730F013, 0xE8BD81F0, 0xE320F000, 0xEAFFFFFE, 0xE320F000, }; static INT32 efuse_check_read_data(UINT16 data) { INT32 i_ret; if ((data & EFUSE_STATUS_VALID) != EFUSE_STATUS_VALID) { i_ret = EFUSE_INACTIVE_ERR; } else { if ((data & EFUSE_STATUS_FORCE_INVALID) == EFUSE_STATUS_FORCE_INVALID) { i_ret = EFUSE_INVALIDATE_ERR; } else { i_ret = EFUSE_SUCCESS; } } return i_ret; } static INT32 efuse_read_data(UINT32 efuse_addr, UINT32 address) { INT32 result; otp_loc(); __asm__("MOV r0, %1\n\t" "MOV r1, %2\n\t" "MOV lr, pc\n\t" "MOV pc, %2\n\t" "MOV %0, r0\n\t" : "=r"(result) : "r"(efuse_addr), "r"(address) : "lr", "r0", "r1"); otp_unl(); return result; } static INT32 efuse_set_key(EFUSE_OTP_KEY_SET_FIELD key_set_index, UINT32 address) { otp_loc(); __asm__("MOV r0, %0\n\t" "MOV r1, %1\n\t" "MOV lr, pc\n\t" "MOV pc, %1\n\t" : : "r"(key_set_index), "r"(address) : "lr", "r0", "r1"); otp_unl(); return E_OK; } static UINT32 dma_get_utilization_data(UINT32 id, UINT32 address) { UINT32 result; __asm__("MOV r0, %1\n\t" "MOV r1, %2\n\t" "MOV lr, pc\n\t" "MOV pc, %2\n\t" "MOV %0, r0\n\t" : "=r"(result) :"r"(id), "r"(address) : "lr", "r0", "r1"); return result; } #define EFUSE_READDATA(addr) efuse_read_data(addr, (UINT32)OTP_READ) /* efuse_read_param_ops efuse read system parameter @note for EFUSE_PARAM_DATA @param[in] param efuse system internal data field @param[in] data data ready to programmed @return -@b EFUSE_SUCCESS success -@b EFUSE_UNKNOW_PARAM_ERR unknow system internal data field -@b EFUSE_INVALIDATE_ERR system internal data field invalidate -@b EFUSE_OPS_ERR efuse operation error */ INT32 efuse_read_param_ops(EFUSE_PARAM_DATA param, UINT16 *data) { INT32 i_ret = EFUSE_SUCCESS; UINT16 tmp_data; UINT32 paramdatacnt; UINT32 pri_param_id_no; switch (param) { case EFUSE_DDR2P_TRIM_DATA: case EFUSE_USBC2_TRIM_DATA: case EFUSE_HDMI_TRIM_DATA: case EFUSE_TVDC_TRIM_DATA: case EFUSE_USBC_TRIM_DATA: case EFUSE_DDRP_TRIM_DATA: case EFUSE_SLVSEC_TRIM_DATA: case EFUSE_VX1_TRIM_DATA: if (EFUSE_HDMI_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_HDMI_PRI; } else if (EFUSE_USBC_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_USB_PRI; } else if (EFUSE_DDRP_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_DDR_DRIVING_PRI; } else if (EFUSE_TVDC_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_TVDEC_PRI; } else if (EFUSE_VX1_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_VX1_PRI; } else if (EFUSE_SLVSEC_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_SLVSEC_PRI; } else if (EFUSE_USBC2_TRIM_DATA == param) { pri_param_id_no = EFUSE_TRIM_DATA_USB2_PRI; } else { pri_param_id_no = EFUSE_TRIM_DATA_DDR2_DRIVING_PRI; } for (paramdatacnt = 0; paramdatacnt < EFUSE_PARAM_OP_CNT; paramdatacnt++) { if (paramdatacnt == 0) { tmp_data = EFUSE_READDATA(pri_param_id_no) & 0xFFFF; } else { tmp_data = (EFUSE_READDATA(pri_param_id_no) >> 16) & 0xFFFF; } if (tmp_data < 0) { i_ret = EFUSE_OPS_ERR; DBG_DUMP("^RERR:PKG UID[%d] op error\r\n", paramdatacnt); continue; } else { *data = (tmp_data & 0x3FFF); i_ret = efuse_check_read_data(tmp_data); if (EFUSE_UNKNOW_PARAM_ERR == i_ret) { DBG_DUMP("^RERR:unknow param error\r\n"); } else if (EFUSE_INVALIDATE_ERR == i_ret) { if (!paramdatacnt) { DBG_IND("^YWRN:[PRI] param invalidate\r\n"); } else { DBG_IND("^YWRN:[SEC] param invalidate\r\n"); } } else if (EFUSE_INACTIVE_ERR == i_ret) { if (!paramdatacnt) { DBG_IND("^YWRN:[PRI] param inactive\r\n"); } else { DBG_IND("^YWRN:[SEC] param inactive\r\n"); } } else { DBG_IND("[%d] success = 0\r\n", paramdatacnt); break; } } } break; default: i_ret = EFUSE_UNKNOW_PARAM_ERR; break; } return i_ret; } INT32 efuse_otp_set_key(EFUSE_OTP_KEY_SET_FIELD key_set_index) { efuse_set_key(key_set_index, (UINT32)OTP_OP); return E_OK; } UINT32 dma_get_utilization(DMA_ID id) { return dma_get_utilization_data(id, (UINT32)&DMA_USAGE_DATA[0]); } static int __init nvt_init_otp_mutex(void) { mutex_init(&otp_lock); return 0; } core_initcall(nvt_init_otp_mutex); EXPORT_SYMBOL(efuse_read_param_ops); EXPORT_SYMBOL(efuse_otp_set_key); EXPORT_SYMBOL(dma_get_utilization);