This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[FAQ] How to run OTP Keywriter on SK-AM64B

Part Number: SK-AM64B
Other Parts Discussed in Thread: TCA9538

Hello TI,

I’m currently working on TI’s SK-AM64B boards, trying to move them from HS-FS to HS-SE mode.

To do so, I’m using the OTP Keywriter tool with MCU+ SDK for AM64x in its latest version (08.05) but unfortunately, it seems to provide only EVM boards support, which is not compatible with SK (when booting tiboot3.bin created by keywriter, i'm stuck at "Starting Keywriting" in the debug console).

Does TI have a new version of OTP Keywriter tool supporting SK-AM64B boards?

Thanks in advance!

  • Hello Lalie,
    It looks like a different IO-expander for turnning ON/OFF VPP is used on SK vs EVM.
    Some additional work is needed to make TCA9538PWR work first in order to run OTP KW on SK board.
    - SK:
    IO EXPANDER TCA9538PWR => VPP_LDO_EN
    - EVM:
    IO EXPANDER TCA6424ARGJR => VPP_LDO_EN
    Best,
    -Hong

  • Hello Hong and thanks for your reply,

    Has this work already been done by somebody?

    If not, I suppose that updating the mcu+sdk/board source code in order to add TCA9538 support will solve my problem, is it right?

    Thanks!

    Lalie

    Update: I finally found how to make KW work for SK-AM64B; is it going to be in TI's next Keywriter release or can I post my solution in this forum ?

    Update 2: To support the SK-AM64B board in OTP Keywriter, you'll need to:
    - add TCA9538 IOExpander code in your {MCU+SDK_INSTALL_PATH}/source/board directory.
    - add SK-AM64B board code in your {MCU+SDK_INSTALL_PATH}/source/security/tifs/sbl_keywriter/am64x-skb/rfss0-0_nortos directory.
    - a script compile_all.sh is also provided there in order to compile newly added TCA9538 support and the keywriter tool for the SK-AM64B.

    Then, all you need to do is to follow OTP Keywriter's User Guide for AM64x EVM and change the paths with SK-B's ones.

    #!/bin/bash
    
    # This script's aim is to compile board sources + keywriter for SK-AM64B
    # Needs to be placed in ${SDK_INSTALL_PATH}/source/security/tifs/sbl_keywriter/am64x-skb/r5fss0-0_nortos/compile_all.sh
    
    # Compiling board libs
    cd ../../../../../board
    make --makefile=makefile.am64x.r5f.ti-arm-clang all -sj PROFILE=debug
    
    # Compiling SK-AM64B OTP Keywriter
    cd ../security/tifs/sbl_keywriter/am64x-skb/r5fss0-0_nortos/ti-arm-clang
    make clean -sj PROFILE=debug && make -sj PROFILE=debug
    
    # Regenerating tiboot3.bin (follow OTP Keywriter's User Guide)
    # cd ../../../scripts/cert_gen
    # ./gen_cert_auto.sh

    Enjoy !

    // {MCU+SDK_INSTALL_PATH}/source/board/ioexp/ioexp_tca9538.c
    
    /* ========================================================================== */
    /*                             Include Files                                  */
    /* ========================================================================== */
    
    #include <board/ioexp/ioexp_tca9538.h>
    
    /* ========================================================================== */
    /*                           Macros & Typedefs                                */
    /* ========================================================================== */
    
    #define TCA9538_REG_INPUT_PORT        (0x00U)
    #define TCA9538_REG_OUTPUT_PORT       (0x01U)
    #define TCA9538_REG_POL_INV_PORT      (0x02U)
    #define TCA9538_REG_CONFIG_PORT       (0x03U)
    
    /* ========================================================================== */
    /*                         Structure Declarations                             */
    /* ========================================================================== */
    
    /* None */
    
    /* ========================================================================== */
    /*                          Function Declarations                             */
    /* ========================================================================== */
    
    /* None */
    
    /* ========================================================================== */
    /*                            Global Variables                                */
    /* ========================================================================== */
    
    /* None */
    
    /* ========================================================================== */
    /*                          Function Definitions                              */
    /* ========================================================================== */
    
    int32_t TCA9538_open(TCA9538_Config *config, const TCA9538_Params *params)
    {
        int32_t         status = SystemP_SUCCESS;
    
        if((NULL == config) || (NULL == params))
        {
            status = SystemP_FAILURE;
        }
        else
        {
            config->params.i2cInstance = params->i2cInstance;
            config->params.i2cAddress  = params->i2cAddress;
            config->lock               = NULL;
            config->i2cHandle          = I2C_getHandle(config->params.i2cInstance);
            if(NULL == config->i2cHandle)
            {
                status = SystemP_FAILURE;
            }
        }
    
        if(status == SystemP_SUCCESS)
        {
            SemaphoreP_constructMutex(&config->lockObj);
            config->lock = &config->lockObj;
            TCA9538_getAttrs(config, &config->attrs);
        }
    
        return (status);
    }
    
    void TCA9538_close(TCA9538_Config *config)
    {
    
        if(NULL == config)
        {
        }
        else
        {
            /* I2C Driver will be closed outside flash */
            config->i2cHandle = NULL;
            if(NULL != config->lock)
            {
                SemaphoreP_destruct(&config->lockObj);
                config->lock = NULL;
            }
        }
    
        return;
    }
    
    int32_t TCA9538_config(TCA9538_Config *config, uint32_t ioIndex, uint32_t mode)
    {
        int32_t         status = SystemP_SUCCESS;
        I2C_Transaction i2cTransaction;
        uint32_t        portPin, i2cAddress;
        uint8_t         buffer[2U] = {0};
    
        if(NULL == config)
        {
            status = SystemP_FAILURE;
        }
        else
        {
            /* Validate input IO number */
            if(ioIndex >= config->attrs.numIo)
            {
                status = SystemP_FAILURE;
            }
        }
    
        if(status == SystemP_SUCCESS)
        {
            /* Each port contains 8 IOs */
            portPin     = ioIndex;
            i2cAddress  = config->params.i2cAddress;
    
            SemaphoreP_pend(&config->lockObj, SystemP_WAIT_FOREVER);
    
            /* Set config register address - needed for next read */
            I2C_Transaction_init(&i2cTransaction);
            buffer[0] = TCA9538_REG_CONFIG_PORT;
            i2cTransaction.writeBuf     = buffer;
            i2cTransaction.writeCount   = 1U;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            /* Read config register value */
            I2C_Transaction_init(&i2cTransaction);
            i2cTransaction.readBuf      = buffer;
            i2cTransaction.readCount    = 1;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            /* Set output or input mode to particular IO pin - read/modify/write */
            I2C_Transaction_init(&i2cTransaction);
            if(TCA9538_MODE_INPUT == mode)
            {
                buffer[1] = buffer[0] | (0x01 << portPin);
            }
            else
            {
                buffer[1] = buffer[0] & ~(0x01 << portPin);
            }
            buffer[0] = TCA9538_REG_CONFIG_PORT;
            i2cTransaction.writeBuf     = buffer;
            i2cTransaction.writeCount   = 2;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            SemaphoreP_post(&config->lockObj);
        }
        return (status);
    }
    
    int32_t TCA9538_setOutput(TCA9538_Config *config, uint32_t ioIndex, uint32_t state)
    {
        int32_t         status = SystemP_SUCCESS;
        I2C_Transaction i2cTransaction;
        uint32_t        port, portPin, i2cAddress;
        uint8_t         buffer[2U] = {0};
    
        if(NULL == config)
        {
            status = SystemP_FAILURE;
        }
        else
        {
            /* Validate input IO number */
            if(ioIndex >= config->attrs.numIo)
            {
                status = SystemP_FAILURE;
            }
        }
    
        if(status == SystemP_SUCCESS)
        {
            /* Each port contains 8 IOs */
            port        = ioIndex >> 3U;        /* /8 gives port */
            portPin     = ioIndex & 0x07U;      /* %8 gives pin within port */
            i2cAddress  = config->params.i2cAddress;
    
            SemaphoreP_pend(&config->lockObj, SystemP_WAIT_FOREVER);
    
            /* Set output prt register address - needed for next read */
            I2C_Transaction_init(&i2cTransaction);
            buffer[0] = TCA9538_REG_OUTPUT_PORT + port;
            i2cTransaction.writeBuf     = buffer;
            i2cTransaction.writeCount   = 1U;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            /* Read config register value */
            I2C_Transaction_init(&i2cTransaction);
            i2cTransaction.readBuf      = buffer;
            i2cTransaction.readCount    = 1;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            /* Set output or input mode to particular IO pin - read/modify/write */
            I2C_Transaction_init(&i2cTransaction);
            if(TCA9538_OUT_STATE_HIGH == state)
            {
                buffer[1] = buffer[0] | (0x01 << portPin);
            }
            else
            {
                buffer[1] = buffer[0] & ~(0x01 << portPin);
            }
            buffer[0] = TCA9538_REG_OUTPUT_PORT;
            i2cTransaction.writeBuf     = buffer;
            i2cTransaction.writeCount   = 2;
            i2cTransaction.slaveAddress = i2cAddress;
            status += I2C_transfer(config->i2cHandle, &i2cTransaction);
    
            SemaphoreP_post(&config->lockObj);
        }
    
        return (status);
    }
    
    void TCA9538_getAttrs(TCA9538_Config *config, TCA9538_Attrs *attrs)
    {
        if(NULL != attrs)
        {
            attrs->numIo = 8U;
        }
    
        return;
    }
    
    void TCA9538_Params_init(TCA9538_Params *params)
    {
        if(NULL != params)
        {
            params->i2cInstance = 0U;
            params->i2cAddress  = 0x70;
        }
    
        return;
    }
    // {MCU+SDK_INSTALL_PATH}/source/board/ioexp/ioexp_tca9538.h
    
    /**
     *  \defgroup BOARD_IO_EXPANDER_TCA9538_MODULE APIs for TCA9538 IO Expander driver
     *  \ingroup BOARD_MODULE
     *
     *  This module contains APIs to program and use I2C based TCA9538 IO Expander
     *  module on the board.
     *
     *  @{
     */
    
    #ifndef IO_EXP_TCA9538_H_
    #define IO_EXP_TCA9538_H_
    
    /* ========================================================================== */
    /*                             Include Files                                  */
    /* ========================================================================== */
    
    #include <stdint.h>
    #include <kernel/dpl/SystemP.h>
    #include <kernel/dpl/SemaphoreP.h>
    #include <drivers/i2c.h>
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /* ========================================================================== */
    /*                           Macros & Typedefs                                */
    /* ========================================================================== */
    
    /**
     *  \anchor TCA9538_Mode
     *  \name IO pin mode - Input or Output
     *  @{
     */
    /** \brief Configure IO pin as input */
    #define TCA9538_MODE_INPUT              (0U)
    /** \brief Configure IO pin as output */
    #define TCA9538_MODE_OUTPUT             (1U)
    /** @} */
    
    /**
     *  \anchor TCA9538_OutState
     *  \name IO pin output state - HIGH or LOW
     *  @{
     */
    /** \brief Configure IO pin output as LOW */
    #define TCA9538_OUT_STATE_LOW           (0U)
    /** \brief Configure IO pin output as HIGH */
    #define TCA9538_OUT_STATE_HIGH          (1U)
    /** @} */
    
    /* ========================================================================== */
    /*                         Structure Declarations                             */
    /* ========================================================================== */
    
    /**
     *  \brief Parameters passed during TCA9538_open()
     */
    typedef struct TCA9538_Params_s
    {
        uint32_t        i2cInstance;
        /**< Underlying peripheral driver instance that is used by the
         *   IO Expander driver incase of I2C controlled IO Expander */
        uint32_t        i2cAddress;
        /**< I2C address for IO expander */
    } TCA9538_Params;
    
    /**
     *  \brief IO Expander device attributes.
     */
    typedef struct TCA9538_Attrs_s
    {
        uint32_t        numIo;
        /**< Number of IO supported by device */
    } TCA9538_Attrs;
    
    /**
     *  \brief IO Expander driver configuration. This is the driver object used to
     *  store state variables
     */
    typedef struct TCA9538_Config_s
    {
        TCA9538_Params      params;
        /**< Parameters */
        TCA9538_Attrs       attrs;
        /**< Attributes */
        I2C_Handle          i2cHandle;
        /**< I2C driver handle */
        void               *lock;
        /**< Mutex to protect IO expander access. */
        SemaphoreP_Object   lockObj;
        /**< Mutex object. */
    } TCA9538_Config;
    
    /* ========================================================================== */
    /*                          Function Declarations                             */
    /* ========================================================================== */
    
    /**
     *  \brief Open TCA9538 driver
     *
     *  Make sure the I2C driver is opened before calling this API.
     *
     *  \param config       [IN] Driver object. Caller need to allocate memory for this.
     *  \param params       [IN] Open parameters
     *
     * \return SystemP_SUCCESS on success, else failure
     */
    int32_t TCA9538_open(TCA9538_Config *config, const TCA9538_Params *params);
    
    /**
     *  \brief Close TCA9538 driver
     *
     *  \param config    [IN] TCA9538 driver config from \ref TCA9538_open
     */
    void TCA9538_close(TCA9538_Config *config);
    
    /**
     * \brief API to set a IO pin of TCA9538 as input or output
     *
     * \param config    [IN] TCA9538 driver config from \ref TCA9538_open
     * \param ioIndex   [IN] Index to the TCA9538 IO which needs to be set/reset.
     * \param mode      [IN] Refer \ref TCA9538_Mode
     *
     * \return SystemP_SUCCESS on success, else failure
     */
    int32_t TCA9538_config(TCA9538_Config *config, uint32_t ioIndex, uint32_t mode);
    
    /**
     * \brief API to set a IO pin of TCA9538 to either HIGH or LOW
     *
     * \param config    [IN] TCA9538 driver config from \ref TCA9538_open
     * \param ioIndex   [IN] Index to the TCA9538 IO which needs to be set/reset.
     * \param state     [IN] Refer \ref TCA9538_OutState
     *
     * \return SystemP_SUCCESS on success, else failure
     */
    int32_t TCA9538_setOutput(TCA9538_Config *config, uint32_t ioIndex, uint32_t state);
    
    /**
     * \brief Returns TCA9538 attributes
     *
     * \param config    [IN] TCA9538 driver config from \ref TCA9538_open
     * \param attrs     [IN/OUT] Structure where the attribute is returned
     *
     */
    void TCA9538_getAttrs(TCA9538_Config *config, TCA9538_Attrs *attrs);
    
    /**
     *  \brief Set default parameters in the \ref TCA9538_Params structure
     *
     *  Call this API to set defaults and then override the fields as needed
     *  before calling  \ref TCA9538_open.
     *
     *  \param params   [OUT] Initialized parameters
     */
    void TCA9538_Params_init(TCA9538_Params *params);
    
    /* ========================================================================== */
    /*                       Static Function Definitions                          */
    /* ========================================================================== */
    
    /* None */
    
    /* ========================================================================== */
    /*                  Internal/Private Structure Declarations                   */
    /* ========================================================================== */
    
    /* None */
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* #ifndef TCA9538_H_ */
    
    /** @} */
    // {MCU+SDK_INSTALL_PATH}/source/security/tifs/sbl_keywriter/am64x-skb/r5fss0-0_nortos/board.c
    
    #include <board/ioexp/ioexp_tca9538.h>
    #include <kernel/dpl/CacheP.h>
    
    /* TP89 VPP CORE on SK-AM64B */
    #define EFUSE_VPP_PIN (4U) // IOExpander-P04
    
    /* *
     * This function sets the VPP voltage for the SoC. 
     * VPP pin is controlled via IO Expander-P4 on SK-AM64B.
     * */
    void keywriter_setVpp()
    {
    	int32_t status;
    
    	TCA9538_Params TCA9538_IOexp_params = 
    	{
    		.i2cInstance = 0,  
    		.i2cAddress = 0x70 
    	}; 
    
    	TCA9538_Config TCA9538_IOexp_config ; 
    
        status = TCA9538_open(&TCA9538_IOexp_config, &TCA9538_IOexp_params);
    
    	/* set VPP core */
        if (status == SystemP_SUCCESS)
    	{	
    		status = TCA9538_config(&TCA9538_IOexp_config, EFUSE_VPP_PIN, TCA9538_MODE_OUTPUT);
    	}
    
        if (status == SystemP_SUCCESS)
    	{	
        	status = TCA9538_setOutput(&TCA9538_IOexp_config, EFUSE_VPP_PIN, TCA9538_OUT_STATE_HIGH);
    	}
    
        TCA9538_close(&TCA9538_IOexp_config);
    	
    	DebugP_assertNoLog(status==SystemP_SUCCESS);
    }
    

  • Hello Lalie,
    It is good news you ported the IO EXPANDER TCA9538PWR, and had OTP KW works on SK-64B board.
    Yes, you're very welcome to share your code change list to benefit other users in the future.
    Best,
    -Hong

  • Hello Lalie,
    Thank you Lalie for sharing the code change list on
    - adding IO EXPANDER TCA9538PWR;
    - checking out OTP key writing to convert HS-FS to HS-SE on SK-64B board.
    These would be a good reference and beneficial for future users.
    FYI. I converted the e2e to a FAQ to make it easier search...
    Best,
    -Hong