/** * @file otp.c * * @brief * This is the OTP driver file. * * \par * ============================================================================ * @n (C) Copyright 2012, Texas Instruments, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * \par */ /* Standard includes */ #include #include /* OTP includes */ #include "otp.h" #include "include/otp_pvt.h" /********************************************************************** ************************** Globals *********************************** **********************************************************************/ /* OTP (EFUSE) control registers according to SPRS671B - TMS320TCI6614 Data Manual */ volatile Otp_ControlOverlay *otpControlRegs = (Otp_ControlOverlay *) 0x02522000; /* Mapping of Key Mgr Keys to their respective rows in the fuseROMs. There are two key * Mgrs and 16 keys per Key Mgr */ const Uint16 otp_KeyMgrKeyToFuseRowMap[EFUSE_MAX_KEY_MGRS][EFUSE_MAX_KEYS_PER_MGR] = {{0x23, 0x27, 0x2B, 0x2F, 0x33, 0x37, 0x3B, 0x3F, 0x43, 0x47, 0x4B, 0x4F, 0x53, 0x57, 0x5B, 0x5F}, {0x63, 0x06, 0x0A, 0x0E, 0x12, 0x16, 0x1A, 0x1E, 0x22, 0x26, 0x2A, 0x2E, 0x32, 0x36, 0x3A, 0x3E}}; /********************************************************************** ********************** Internal Functions ********************************* **********************************************************************/ void Otp_delay (void) { Uint32 i; for(i = 0; i < 100000; i++) { asm(" NOP 9 "); } } Otp_Status Otp_checkForWriteError (Uint32 errorStatus) { Uint16 i; Uint32 extractedErrorStatus = Otp_status_INSTRUCTION_SUCCESSFUL; /* Return the first fuse chain error */ for (i = 0; i < EFUSE_ERROR_STATUS_NUM_FUSE_CHAINS; i++) { if (extractedErrorStatus = EFUSE_ERROR_STATUS_WRITE(errorStatus, i)) { return ((Otp_Status) extractedErrorStatus); } } /* If no error found return the extracedErrorStatus anyway, which will be instruction successful */ return ((Otp_Status) extractedErrorStatus); } Otp_Status Otp_checkForReadError (Uint32 errorStatus) { Uint16 i; Uint32 extractedErrorStatus = Otp_status_INSTRUCTION_SUCCESSFUL; /* Return the first fuse chain error */ for (i = 0; i < EFUSE_ERROR_STATUS_NUM_FUSE_CHAINS; i++) { if (extractedErrorStatus = EFUSE_ERROR_STATUS_READ(errorStatus, i)) { return ((Otp_Status) extractedErrorStatus); } } /* If no error found return the extracedErrorStatus anyway, which will be instruction successful */ return ((Otp_Status) extractedErrorStatus); } Otp_Status Otp_write (Uint32 *data, Uint16 size, Uint16 fuseRomBlock, Uint16 fuseRomRow, Otp_writeCfg *otpWrCfg) { Uint32 i; Uint32 status; Uint32 bitReversedData; Uint32 rowAddress; Uint32 readData; /* Make sure if read protection is requested to be set that write protection is set as well */ if (otpWrCfg->readProtect && !otpWrCfg->writeProtect) { return (Otp_status_READ_PROTECT_WITHOUT_WRITE_PROTECT); } /* Configure the OTP registers for write/read */ /* Set key code which protects fuseROM during power-up */ otpControlRegs->otpKey= EFUSE_KEY_CODE; /* Set the read data bit and the read pulse width */ otpControlRegs->otpRead = (EFUSE_CLOCK_READ_PULSE_WIDTH|EFUSE_READ_READ_DATA_BIT); /* Perform the number of writes specified in the size parameter */ for(i = 0; i < size; i++) { /* Configure the eFuse control registers for write */ /* Clear the error status register before each write and read */ otpControlRegs->otpErrorStatus = 0x00000000; /* Start with the specified eFuse row address and work backwards */ rowAddress = fuseRomRow-i; /* Choose the fuseROM block based on the input parameter and set * the address register */ if (fuseRomBlock == EFUSE_OTP_2_BLOCK) { rowAddress |= EFUSE_ADDRESS_FUSEROM_BLOCK_1; } otpControlRegs->otpAddress = rowAddress; #if 1 /* TEST READ */ /* Clear the data lower register since the read data will be placed there */ otpControlRegs->otpDataLower = 0x00000000; /* Set the program register for read adding in the write attempts */ otpControlRegs->otpProgram = (EFUSE_PROGRAM_READ_CONFIG | (otpWrCfg->maxWrAttempts << EFUSE_PROGRAM_WR_ATTEMPT_SHIFT)); /* Peform the read from the row address that was just written to */ otpControlRegs->otpInstructionDumpword = (EFUSE_INSTRUCTION_READ|rowAddress); /* Allow time for the read to occur */ Otp_delay(); /* Check for an error during read. Return if error occurred */ if (status = Otp_checkForReadError(otpControlRegs->otpErrorStatus)) { return ((Otp_Status) status); } /* Compare the read data with what was originally written */ readData = _bitr(otpControlRegs->otpDataLower); printf("read data %x\n", readData); /* Clear the error status register before each write and read */ otpControlRegs->otpErrorStatus = 0x00000000; #endif /* Set the program register for write adding in the write attempts */ otpControlRegs->otpProgram = (EFUSE_PROGRAM_WRITE_CONFIG | (otpWrCfg->maxWrAttempts << EFUSE_PROGRAM_WR_ATTEMPT_SHIFT)); /* Configure the data upper and lower registers with the data and protection * settings. Reverse the bit order of the data word to be written. All eFuse chain bits * are reversed */ bitReversedData = _bitr(data[i]); otpControlRegs->otpDataLower = bitReversedData; if (otpWrCfg->writeProtect && otpWrCfg->readProtect) { otpControlRegs->otpDataUpper = EFUSE_DATA_UPPER_NO_REP_WR_RD_PROT; } else if (otpWrCfg->writeProtect) { otpControlRegs->otpDataUpper = EFUSE_DATA_UPPER_NO_REP_WR_PROT; } else { otpControlRegs->otpDataUpper = 0x00000000; } /* Perform the write by writing the "write" command to the instruction register */ otpControlRegs->otpInstructionDumpword = EFUSE_INSTRUCTION_PROGRAM; /* Allow time for the write to occur */ Otp_delay(); /* Check for an error during write. Return if error occurred */ if (status = Otp_checkForWriteError(otpControlRegs->otpErrorStatus)) { return ((Otp_Status) status); } /* Read back from the location to make sure the data was written */ /* Clear the error status register before each write and read */ otpControlRegs->otpErrorStatus = 0x00000000; /* Clear the data lower register since the read data will be placed there */ otpControlRegs->otpDataLower = 0x00000000; /* Set the program register for read adding in the write attempts */ otpControlRegs->otpProgram = (EFUSE_PROGRAM_READ_CONFIG | (otpWrCfg->maxWrAttempts << EFUSE_PROGRAM_WR_ATTEMPT_SHIFT)); /* Peform the read from the row address that was just written to */ otpControlRegs->otpInstructionDumpword = (EFUSE_INSTRUCTION_READ|rowAddress); /* Allow time for the read to occur */ Otp_delay(); /* Check for an error during read. Return if error occurred */ if (status = Otp_checkForReadError(otpControlRegs->otpErrorStatus)) { return ((Otp_Status) status); } /* Compare the read data with what was originally written */ readData = _bitr(otpControlRegs->otpDataLower); if (readData != data[i]) { return(Otp_status_READ_DATA_NOT_SAME_AS_WRITTEN); } } /* Clear the program register when all writes and reads are complete */ otpControlRegs->otpProgram = 0x00000000; return (Otp_status_INSTRUCTION_SUCCESSFUL); } /********************************************************************** *********************** Application visible APIs *************************** **********************************************************************/ Otp_Status Otp_smekWrite (Uint32 *smekDataPtr, Otp_writeCfg *otpWrCfg) { return (Otp_write(smekDataPtr, OTP_SMEK_SIZE_32BIT_WORDS, EFUSE_SMEK_BLOCK, EFUSE_SMEK_ROW, otpWrCfg)); } Otp_Status Otp_smpkWrite (Uint32 *smpkDataPtr, Otp_writeCfg *otpWrCfg) { return (Otp_write(smpkDataPtr, OTP_SMPK_SIZE_32BIT_WORDS, EFUSE_SMPK_BLOCK, EFUSE_SMPK_ROW, otpWrCfg)); } Otp_Status Otp_otpWrite (Uint32 *otpDataPtr, Uint16 otpKeyMgr, Uint16 otpKey, Otp_writeCfg *otpWrCfg) { Uint16 otpKeyBlock; Uint16 otpKeyRow = otp_KeyMgrKeyToFuseRowMap[otpKeyMgr][otpKey]; Otp_Status retVal; if (EFUSE_OTP_KEY_CHECK(otpKeyMgr, otpKey)) { return (Otp_status_KEY_MGR_OR_KEY_OUT_OF_RANGE); } /* All Key Mgr 0 keys are in fuseROM block 0, as well as Key Mgr 1, Key 0 */ if ((otpKeyMgr == 0) || ((otpKeyMgr == 1) && (otpKey == 0))) { /* Find the row for the key to be programmed */ otpKeyBlock = EFUSE_OTP_1_BLOCK; return (Otp_write(otpDataPtr, EFUSE_OTP_SIZE_32BIT_WORDS, otpKeyBlock, otpKeyRow, otpWrCfg)); } else if ((otpKeyMgr == 1) && (otpKey == 1)) { /* Key Mgr 1, Key 1 is split between two fuseROM blocks. Need to write half of the key to * each block */ /* Write the second fuseROM block first */ otpKeyBlock = EFUSE_OTP_2_BLOCK; if (retVal = Otp_write(&otpDataPtr[2], (EFUSE_OTP_SIZE_32BIT_WORDS/2), otpKeyBlock, otpKeyRow, otpWrCfg)) { /* Error during first write */ return (retVal); } /* Complete the second half of the write. Row should be last row in fuseROM 0 */ otpKeyBlock = EFUSE_OTP_1_BLOCK; otpKeyRow = EFUSE_OTP_1_BASE_ROW; return (Otp_write(otpDataPtr, (EFUSE_OTP_SIZE_32BIT_WORDS/2), otpKeyBlock, otpKeyRow, otpWrCfg)); } else { /* Handle remaining Keys in Key Mgr 1 */ otpKeyBlock = EFUSE_OTP_2_BLOCK; return (Otp_write(otpDataPtr, EFUSE_OTP_SIZE_32BIT_WORDS, otpKeyBlock, otpKeyRow, otpWrCfg)); } } Otp_Status Otp_otpSetPrivate (Uint16 otpKeyMgr, Uint16 otpKey, Otp_writeCfg *otpWrCfg) { Uint32 setPrivateVal = 0x00000000; if (EFUSE_OTP_KEY_CHECK(otpKeyMgr, otpKey)) { return (Otp_status_KEY_MGR_OR_KEY_OUT_OF_RANGE); } /* Form the value to write into the eFuse to set the private bit for the * OTP key specified. Any bits that are 0 will not be reflected in the eFuse * when the write occurs * * Bit 31 sets Key Mgr 1 - Key 15 to private * Bit 30 sets Key Mgr 1 - Key 14 to private * ... * Bit 1 sets Key Mgr 0 - Key 1 to private * Bit 0 sets Key Mgr 0 - Key 0 to private */ setPrivateVal = 0x1 << (otpKey << otpKeyMgr); return (Otp_write(&setPrivateVal, EFUSE_PUBLIC_PRIVATE_ENABLE_SIZE_32BIT_WORDS, EFUSE_PUBLIC_PRIVATE_ENABLE_BLOCK, EFUSE_PUBLIC_PRIVATE_ENABLE_ROW, otpWrCfg)); }