/**
 * \addtogroup BSP
 * \{
 * \addtogroup DEVICES
 * \{
 * \addtogroup OTPC
 * \{
 */

/**
 ****************************************************************************************
 *
 * @file hw_otpc.h
 *
 * @brief Definition of API for the OTP Controller driver.
 *
 * Copyright (c) 2016, Dialog Semiconductor
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the copyright holder 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 HOLDER 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.
 *
 ****************************************************************************************
 */

#ifndef HW_OTPC_H_
#define HW_OTPC_H_

#if dg_configUSE_HW_OTPC

#include <stdbool.h>
#include <stdint.h>
#include <black_orca.h>


/**
 * \brief Get the mask of a field of an OTPC register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to access
 *
 */
#define HW_OTPC_REG_FIELD_MASK(reg, field) \
        (OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Msk)

/**
 * \brief Get the bit position of a field of an OTPC register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to access
 *
 */
#define HW_OTPC_REG_FIELD_POS(reg, field) \
        (OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Pos)

/**
 * \brief Prepare (i.e. shift and mask) a value to be used for an OTPC register field.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to access
 * \param [in] val is the value to prepare
 *
 */
#define HW_OTPC_FIELD_VAL(reg, field, val) \
        (((val) << HW_OTPC_REG_FIELD_POS(reg, field)) & HW_OTPC_REG_FIELD_MASK(reg, field))

/**
 * \brief Get the value of a field of an OTPC register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to write
 *
 * \return the value of the register field
 *
 */
#define HW_OTPC_REG_GETF(reg, field) \
        ((OTPC->OTPC_##reg##_REG & (OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Msk)) >> (OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Pos))

/**
 * \brief Set the value of a field of an OTPC register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to write
 * \param [in] new_val is the value to write
 *
 */
#define HW_OTPC_REG_SETF(reg, field, new_val) \
        OTPC->OTPC_##reg##_REG = ((OTPC->OTPC_##reg##_REG & ~(OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Msk)) | \
        ((OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Msk) & ((new_val) << (OTPC_OTPC_##reg##_REG_##OTPC_##reg##_##field##_Pos))))


/**
 * \brief OTP Controller mode
 */
typedef enum {
        HW_OTPC_MODE_STBY       = 0,
        HW_OTPC_MODE_MREAD      = 1,
        HW_OTPC_MODE_MPROG      = 2,
        HW_OTPC_MODE_AREAD      = 3,
        HW_OTPC_MODE_APROG      = 4,
        HW_OTPC_MODE_TBLANK     = 5,
        HW_OTPC_MODE_TDEC       = 6,
        HW_OTPC_MODE_TWR        = 7
} HW_OTPC_MODE;

#define MAX_RR_AVAIL            (8)

#define OTP_CLK_IS_16M          (0)
#define OTP_CLK_IS_32M          (1)
#define OTP_CLK_IS_48M          (2)


/**
 * \brief Word inside cell to program/read
 *
 * Cell contents in memory starts with low word (i.e. to program/read both words in cell at once,
 * HW_OTPC_WORD_LOW should be used for addressing).
 *
 */
typedef enum {
        HW_OTPC_WORD_LOW = 0,
        HW_OTPC_WORD_HIGH = 1
} HW_OTPC_WORD;


/**
 * \brief System clock frequency in MHz
 *
 */
typedef enum {
        HW_OTPC_SYS_CLK_FREQ_1 = 0,
        HW_OTPC_SYS_CLK_FREQ_2 = 1,
        HW_OTPC_SYS_CLK_FREQ_3 = 2,
        HW_OTPC_SYS_CLK_FREQ_4 = 3,
        HW_OTPC_SYS_CLK_FREQ_6 = 4,
        HW_OTPC_SYS_CLK_FREQ_8 = 5,
        HW_OTPC_SYS_CLK_FREQ_12 = 6,
        HW_OTPC_SYS_CLK_FREQ_16 = 7,
        HW_OTPC_SYS_CLK_FREQ_24 = 8,
        HW_OTPC_SYS_CLK_FREQ_32 = 9,
        HW_OTPC_SYS_CLK_FREQ_48 = 10
} HW_OTPC_SYS_CLK_FREQ;

/*
 * Reset values of OTPC registers
 */
#define OTPC_MODE_REG_RESET                  (0x00000000)
#define OTPC_PCTRL_REG_RESET                 (0x00000000)
#define OTPC_STAT_REG_RESET                  (0x00000051)
#define OTPC_AHBADR_REG_RESET                (0x07FC0000)
#define OTPC_CELADR_REG_RESET                (0x00000000)
#define OTPC_NWORDS_REG_RESET                (0x00000000)
#define OTPC_FFPRT_REG_RESET                 (0x00000000)
#define OTPC_FFRD_REG_RESET                  (0x00000000)
#define OTPC_PWORDL_REG_RESET                (0x00000000)
#define OTPC_PWORDH_REG_RESET                (0x00000000)
#define OTPC_TIM1_REG_RESET                  (0x1A104F20)
#define OTPC_TIM2_REG_RESET                  (0x00010000)

/**
 * \brief Convert system clock frequency expressed in MHz to equivalent HW_OTPC_SYS_CLK_FREQ value
 *
 * \sa HW_OTPC_SYS_CLK_FREQ
 */
__RETAINED_CODE HW_OTPC_SYS_CLK_FREQ hw_otpc_convert_sys_clk_mhz(uint32_t clk_freq);


/**
 * \brief Initialize the OTP Controller.
 *
 * \warning The AHB clock must be up to 48MHz! It is a responsibility of the caller to check this!
 *
 */
static inline void hw_otpc_init(void)
{
#if (dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A)
        /*
         * Reset OTP controller
         */
        OTPC->OTPC_MODE_REG = HW_OTPC_MODE_STBY;    // Set OTPC to standby
        REG_SET_BIT(CRG_TOP, SYS_CTRL_REG, OTPC_RESET_REQ); // Reset the OTP Controller
        REG_CLR_BIT(CRG_TOP, SYS_CTRL_REG, OTPC_RESET_REQ);// Get the OTP Controller out of Reset

        /*
         * Initialize the POR registers
         */
        OTPC->OTPC_NWORDS_REG = OTPC_NWORDS_REG_RESET;
        OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_RESET;
        OTPC->OTPC_TIM2_REG = OTPC_TIM2_REG_RESET;
#endif
        /*
         * Enable OTPC clock
         */
        CRG_TOP->CLK_AMBA_REG |= 1 << REG_POS(CRG_TOP, CLK_AMBA_REG, OTP_ENABLE);
}

/**
 * \brief Close the OTP Controller.
 *
 */
static inline void hw_otpc_close(void)
{
        /*
         * Disable OTPC clock
         */
        CRG_TOP->CLK_AMBA_REG &= ~REG_MSK(CRG_TOP, CLK_AMBA_REG, OTP_ENABLE);
}

/**
 * \brief Check if the OTP Cotnroller is active.
 *
 * \return 1 if it is active, else 0.
 *
 */
static inline uint32_t hw_otpc_is_active(void) __attribute__((always_inline));

static inline uint32_t hw_otpc_is_active(void)
{
        /*
         * Disable OTPC clock
         */
        return !!(CRG_TOP->CLK_AMBA_REG & REG_MSK(CRG_TOP, CLK_AMBA_REG, OTP_ENABLE));
}

/**
 * \brief Put the OTP Controller in STBY mode and turn-off clock.
 *
 */
void hw_otpc_disable(void);

/**
 * \brief Set the access speed of the OTP Controller based on the system clock.
 *
 * \details Switch from PLL to XTAL : call function after clock switch with high_clk_speed == false
 *          Switch from XTAL to PLL : call function before clock switch with high_clk_speed == true
 *
 * \param [in] clk_speed The frequency of the system clock: 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 and 48.
 *
 * \warning The OTP clock must have been enabled (OTP_ENABLE == 1).
 *          (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
__RETAINED_CODE void hw_otpc_set_speed(HW_OTPC_SYS_CLK_FREQ clk_speed);

/**
 * \brief Set or reset the power saving mode of the OTP Controller.
 *
 * \param [in] inactivity_period The number of HCLK cycles to remain powered while no access is made.
 *                               If set to 0, this feature is disabled.
 *
 * \warning The hw_otpc_init() and hw_otpc_set_speed() must have been called.
 *          (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 */
void hw_otpc_power_save(uint32_t inactivity_period);

/**
 * \brief Get the number of the Repair Records.
 *
 * \return The number of used Repair Records.
 *
 * \warning The hw_otpc_init() and hw_otpc_set_speed() must have been called. The OTPC mode will be reset.
 *          (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 */
uint32_t hw_otpc_num_of_rr(void);

/**
 * \brief Program manually an OTP entry.
 *
 * \param [in] cell_offset Cell offset to be programmed.
 *
 * \param [in] pword_l   The low 32-bits of the 64-bit word to be written.
 *
 * \param [in] pword_h   The high 32-bits of the 64-bit word to be written.
 *
 * \param [in] use_rr    Use a Repair Record if writing fails when true.
 *
 * \return true for success, false for failure
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_manual_word_prog(uint32_t cell_offset, uint32_t pword_l, uint32_t pword_h, bool use_rr);

/**
 * \brief Program manually an OTP block.
 *
 * \param [in] p_data       The start address of the data to copy from.
 *
 * \param [in] cell_offset  Cell offset to start programming from.
 *
 * \param [in] cell_word    Word inside cell to start programming from.
 *
 * \param [in] num_of_words The actual number of the words to be written.
 *
 * \param [in] use_rr       true to use Repair Records if writing fails
 *
 * \return true for success, false for failure
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_manual_prog(const uint32_t *p_data, uint32_t cell_offset, HW_OTPC_WORD cell_word,
                uint32_t num_of_words, bool use_rr);

/**
 * \brief Put the OTP in manual read mode.
 *
 * \details In manual read mode the OTP is memory mapped to the address MEMORY_OTP_BASE. So, any
 *          access to this memory space results into reading from the OTP. The caller must make sure
 *          that the function hw_otpc_manual_read_off() is called when reading from the OTP is
 *          finished.
 *
 * \param [in] spare_rows true to access the spare rows area, false to access normal memory cell
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
void hw_otpc_manual_read_on(bool spare_rows);

/**
 * \brief Take the OTP out of manual read mode and put it in STBY.
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
void hw_otpc_manual_read_off(void);

/**
 * \brief Program manually a Repair Record in the OTP's spare area.
 *
 * \param [in] cell_offset The address programmed with errors.
 * \param [in] pword_l   The low 32-bits of the 64-bit word to be written.
 * \param [in] pword_h   The high 32-bits of the 64-bit word to be written.
 * \return true for success, false for failure
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_write_rr(uint32_t cell_offset, uint32_t pword_l, uint32_t pword_h);

/**
 * \brief Program OTP via DMA.
 *
 * \param [in] p_data    The start address of the data in RAM.
 *
 * \param [in] cell_offset Cell offset to be programmed.
 *
 * \param [in] cell_word   Word inside cell to be programmed.
 *
 * \param [in] num_of_words The actual number of the words to be written. The driver will write this
 *                          value minus 1 to the specific register.
 *
 * \param [in] spare_rows true to access the spare rows area, false to access normal memory cell
 *
 * \return true for success, false for failure (the programming should be retried or fixed!)
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_dma_prog(const uint32_t *p_data, uint32_t cell_offset, HW_OTPC_WORD cell_word,
                uint32_t num_of_words, bool spare_rows);

/**
 * \brief Read OTP via DMA.
 *
 * \param [in] p_data    The address in RAM to write the data to.
 *
 * \param [in] cell_offset Cell offset to be programmed.
 *
 * \param [in] cell_word   Word inside cell to be programmed.
 *
 * \param [in] num_of_words The actual number of the words to be read. The driver will write this
 *                          value minus 1 to the specific register.
 *
 * \param [in] spare_rows true to access the spare rows area, false to access normal memory cell
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
void hw_otpc_dma_read(uint32_t *p_data, uint32_t cell_offset, HW_OTPC_WORD cell_word,
                uint32_t num_of_words, bool spare_rows);

/**
 * \brief Program OTP via FIFO (Auto mode).
 *
 * \param [in] p_data    The start address of the data in RAM.
 *
 * \param [in] cell_offset Cell offset to be programmed.
 *
 * \param [in] cell_word   Word inside cell to be programmed.
 *
 * \param [in] num_of_words The actual number of the words to be written. The driver will write this
 *                          value minus 1 to the specific register.
 *
 * \param [in] spare_rows true to access the spare rows area, false to access normal memory cell
 *
 * \return true for success, false for failure
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_fifo_prog(const uint32_t *p_data, uint32_t cell_offset, HW_OTPC_WORD cell_word,
                uint32_t num_of_words, bool spare_rows);

/**
 * \brief Read OTP via FIFO.
 *
 * \param [in] p_data    The address in RAM to write the data to.
 *
 * \param [in] cell_offset Cell offset to be programmed.
 *
 * \param [in] cell_word   Word inside cell to be programmed.
 *
 * \param [in] num_of_words The actual number of the words to be read. The driver will write this
 *                          value minus 1 to the specific register.
 *
 * \param [in] spare_rows true to access the spare rows area, false to access normal memory cell
 *
 * \return true for success, false for failure
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
bool hw_otpc_fifo_read(uint32_t *p_data, uint32_t cell_offset, HW_OTPC_WORD cell_word,
                uint32_t num_of_words, bool spare_rows);

/**
 * \brief Prepare OTPC to execute an OTP copy after wakeup.
 *
 * \param [in] num_of_bytes The actual number of the bytes to be copied. The driver will write the
 *                          proper value to the specific register.
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
void hw_otpc_prepare(uint32_t num_of_bytes);

/**
 * \brief Cancel any previous preparations of the OTPC for executing an OTP copy after wakeup.
 *
 * \warning The hw_otpc_init() and the hw_otpc_set_speed() must have been called and the mode must
 *          be STBY. (Note: the hw_otpc_set_speed() must be called only once when the PLL is used or
 *          during each clock switch when both PLL and XTAL16 are used, since the register bits it
 *          modifies are retained.)
 *
 */
void hw_otpc_cancel_prepare(void);

/**
 * \brief Get cell memory address
 *
 * Returns mapped memory address for given cell, can be used for e.g. manual reading.
 *
 * \param [in] cell_offset cell offset
 *
 * \return cell memory address
 *
 * \sa hw_otpc_manual_read_on
 *
 */
static inline void *hw_otpc_cell_to_mem(uint32_t cell_offset)
{
        return (void *) (MEMORY_OTP_BASE + (cell_offset << 3)); // cell size is 8 bytes
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// TEST FUNCTIONALITY
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * \brief Execution of the BLANK test.
 *
 * \return The test result.
 *         <ul>
 *             <li> 0, if the test succeeded
 *             <li> 1, if the test 1 failed
 *         </ul>
 *
 */
int hw_otpc_blank(void);

/**
 * \brief Execution of the TDEC test.
 *
 * \return The test result.
 *         <ul>
 *             <li> 0, if the test succeeded
 *             <li> 1, if the test 1 failed
 *         </ul>
 *
 */
int hw_otpc_tdec(void);

/**
 * \brief Execution of the TWR test.
 *
 * \return The test result.
 *         <ul>
 *             <li> 0, if the test succeeded
 *             <li> 1, if the test 1 failed
 *         </ul>
 *
 */
int hw_otpc_twr(void);

#endif /* dg_configUSE_HW_OTPC */

#endif /* HW_OTPC_H_ */

/**
\}
\}
\}
*/
