blob: e85461e34ce8488d6dee04c0b4eebf69ef2b22e5 [file] [log] [blame]
/**
* \addtogroup BSP
* \{
* \addtogroup DEVICES
* \{
* \addtogroup OTPC
* \{
*/
/**
****************************************************************************************
*
* @file hw_otpc.c
*
* @brief Implementation of the OTP Controller Low Level 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.
*
*
****************************************************************************************
*/
#if dg_configUSE_HW_OTPC
#include <stdint.h>
#include "hw_otpc.h"
/*
* Local variables
*/
/*
* 1MHz: 1 cycle = 1us ==>
* 25ns : 1us, 200ns : 1us, 500ns : 1us, 1us : 2us, 5us : 5us, 2us : 3us, blanc : 1us
*
* 2MHz: 1 cycle = 500ns ==>
* 25ns : 500ns, 200ns : 500ns, 500ns : 1us, 1us : 1.5us, 5us : 5us, 2us : 2.5us, blanc : 500ns
*
* 3MHz: 1 cycle = 333ns ==>
* 25ns : 333ns, 200ns : 333ns, 500ns : 666ns, 1us : 1.33us, 5us : 5us, 2us : 2.33us, blanc : 333ns
*
* 4MHz: 1 cycle = 250ns ==>
* 25ns : 250ns, 200ns : 250ns, 500ns : 750ns, 1us : 1.25ns, 5us : 5us, 2us : 2.25ns, blanc : 250ns
*
* 6MHz: 1 cycle = 167ns ==>
* 25ns : 167ns, 200ns : 333ns, 500ns : 667ns, 1us : 1.167ns, 5us : 5us, 2us : 2.167ns, blanc : 167ns
*
* 8MHz: 1 cycle = 125ns ==>
* 25ns : 125ns, 200ns : 250ns, 500ns : 625ns, 1us : 1.125ns, 5us : 5us, 2us : 2.125ns, blanc : 125ns
*
* 12MHz: 1 cycle = 83.33ns ==>
* 25ns : 83ns, 200ns : 250ns, 500ns : 583ns, 1us : 1.083ns, 5us : 5us, 2us : 2.083ns, blanc : 167ns
*
* 16MHz: 1 cycle = 62.5ns ==>
* 25ns : 62ns, 200ns : 250ns, 500ns : 562ns, 1us : 1.062ns, 5us : 5us, 2us : 2.062ns, blanc : 125ns
*
* 24MHz: 1 cycle = 41.67ns ==>
* 25ns : 41ns, 200ns : 208ns, 500ns : 541ns, 1us : 1.041ns, 5us : 5us, 2us : 2.041ns, blanc : 125ns
*
* 32MHz: 1 cycle = 31.25ns ==>
* 25ns : 31ns, 200ns : 219ns, 500ns : 531ns, 1us : 1.031ns, 5us : 5us, 2us : 2.031ns, blanc : 125ns
*
* 48MHz: 1 cycle = 41.67ns ==>
* 25ns : 41ns, 200ns : 208ns, 500ns : 521ns, 1us : 1.021ns, 5us : 5us, 2us : 2.021ns, blanc : 125ns
*
*/
const uint32_t tim1[11] =
{
/* 1 MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(4 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(2 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 2MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(2 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(9 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(4 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 3MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(3 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(14 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(6 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 4MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(2 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(4 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(19 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(8 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 6MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(3 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(6 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(29 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(12 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 8MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(4 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(8 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(39 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(16 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 12MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(2 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(6 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(12 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(59 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(24 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 16MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(3 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(8 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(16 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(79 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(32 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 24MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(4 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(12 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(24 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(119 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(48 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 32MHz */
(0 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(6 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(16 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(32 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(159 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(64 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos),
/* 48MHz */
(1 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_25NS_Pos) |
(9 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_200NS_Pos) |
(24 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_500NS_Pos) |
(48 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_1US_Pos) |
(239 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_PW_Pos) |
(96 << OTPC_OTPC_TIM1_REG_OTPC_TIM1_CC_T_CADX_Pos)
};
static const uint8_t tim2[HW_OTPC_SYS_CLK_FREQ_48 + 1] =
{ 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 5 };
/*
* Forward declarations
*/
/*
* Inline helpers
*/
/**
* Wait for programming to finish.
*/
__STATIC_INLINE void wait_for_prog_done(void)
{
while (!(OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, PRDY)))
;
}
/**
* Wait for AREAD or APROG to finish.
*/
__STATIC_INLINE void wait_for_auto_done(void)
{
while (!(OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, ARDY)))
;
}
/**
* Check for correctable or uncorrectable programming error.
*/
__STATIC_INLINE bool have_prog_error(void)
{
return OTPC->OTPC_STAT_REG &
(HW_OTPC_FIELD_VAL(STAT, PERR_UNC, 1) | HW_OTPC_FIELD_VAL(STAT, PERR_COR, 1));
}
/*
* Assertion macros
*/
/*
* Make sure that the OTP clock is enabled
*/
#define ASSERT_WARNING_OTP_CLK_ENABLED \
ASSERT_WARNING(CRG_TOP->CLK_AMBA_REG & REG_MSK(CRG_TOP, CLK_AMBA_REG, OTP_ENABLE))
/*
* Make sure that the OTPC is in given state
*/
#define ASSERT_WARNING_OTPC_MODE(s) \
ASSERT_WARNING((OTPC->OTPC_MODE_REG & HW_OTPC_REG_FIELD_MASK(MODE, MODE)) \
== HW_OTPC_FIELD_VAL(MODE, MODE, s))
/*
* Make sure that the cell address is valid
*/
#define ASSERT_CELL_OFFSET_VALID(off) \
ASSERT_WARNING(off < 0x2000)
/*
* Make sure val is non-zero and less than maximum
*/
#define ASSERT_WARNING_NONZERO_RANGE(val, maximum) \
do { \
ASSERT_WARNING(val); \
ASSERT_WARNING((val) < (maximum)); \
} while (0)
/*
* Function definitions
*/
__RETAINED_CODE HW_OTPC_SYS_CLK_FREQ hw_otpc_convert_sys_clk_mhz(uint32_t clk_freq)
{
HW_OTPC_SYS_CLK_FREQ f;
/* Value to convert must be at most 48 MHz */
ASSERT_WARNING(clk_freq <= 48);
if (--clk_freq < 4)
{
/*
* cover 1, 2, 3, 4
*/
f = (HW_OTPC_SYS_CLK_FREQ)clk_freq;
}
else
{
clk_freq -= 5;
/*
* remaining valid values:
* - 0 (initially 6)
* - 2 (initially 8)
* - 6 (initially 12)
* - 10 (initially 16)
* - 18 (initially 24)
* - 26 (initially 32)
* - 42 (initially 48)
*/
/* no odd valid values any more */
ASSERT_WARNING(!(clk_freq & 1));
clk_freq >>= 1;
/*
* remaining valid values:
* - 0 (initially 6)
* - 1 (initially 8)
* - 3 (initially 12)
* - 5 (initially 16)
* - 9 (initially 24)
* - 13 (initially 32)
* - 21 (initially 48)
*/
if (clk_freq > 8)
{
/*
* remaining valid values:
* - 9 (initially 24)
* - 13 (initially 32)
* - 21 (initially 48)
*/
if (clk_freq > 16)
{
ASSERT_WARNING(clk_freq == 21);
f = HW_OTPC_SYS_CLK_FREQ_48;
}
else
{
clk_freq -= 8;
/*
* remaining valid values:
* - 1 (initially 24)
* - 5 (initially 32)
*/
ASSERT_WARNING((clk_freq == 1) || (clk_freq == 5));
if (clk_freq < 4)
{
f = (HW_OTPC_SYS_CLK_FREQ)HW_OTPC_SYS_CLK_FREQ_24;
}
else
{
f = (HW_OTPC_SYS_CLK_FREQ)HW_OTPC_SYS_CLK_FREQ_32;
}
}
}
else
{
/*
* remaining valid values:
* - 0 (initially 6)
* - 1 (initially 8)
* - 3 (initially 12)
* - 5 (initially 16)
*/
if (clk_freq > 2)
{
ASSERT_WARNING((clk_freq == 3) || (clk_freq == 5));
if (clk_freq > 4)
{
f = (HW_OTPC_SYS_CLK_FREQ)HW_OTPC_SYS_CLK_FREQ_16;
}
else
{
f = (HW_OTPC_SYS_CLK_FREQ)HW_OTPC_SYS_CLK_FREQ_12;
}
}
else
{
f = (HW_OTPC_SYS_CLK_FREQ)(HW_OTPC_SYS_CLK_FREQ_6 + clk_freq);
}
}
}
return f;
}
void hw_otpc_disable(void)
{
/*
* Enable OTPC clock
*/
hw_otpc_init();
/*
* set OTPC to stand-by mode
*/
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
/*
* Disable OTPC clock
*/
hw_otpc_close();
}
__RETAINED_CODE void hw_otpc_set_speed(HW_OTPC_SYS_CLK_FREQ clk_speed)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
/*
* Set access speed
*/
ASSERT_WARNING(clk_speed <= HW_OTPC_SYS_CLK_FREQ_48);
OTPC->OTPC_TIM1_REG = tim1[clk_speed];
HW_OTPC_REG_SETF(TIM2, CC_T_BCHK, tim2[clk_speed]);
}
void hw_otpc_power_save(uint32_t inactivity_period)
{
/* Only go to power save for an inactivity_period < 1024 */
ASSERT_WARNING(inactivity_period < 1024);
HW_OTPC_REG_SETF(TIM2, CC_STBY_THR, inactivity_period);
}
uint32_t hw_otpc_num_of_rr(void)
{
unsigned int i;
volatile uint32_t *ptr = (volatile uint32_t *)MEMORY_OTP_BASE;
ASSERT_WARNING_OTP_CLK_ENABLED;
// STBY mode
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
// The access will be performed in the spare rows
HW_OTPC_REG_SETF(MODE, USE_SP_ROWS, 1);
// MREAD mode
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_MREAD);
// Read Records
i = 0;
while ((i < MAX_RR_AVAIL) && (ptr[0x9e - 4 * i] & 0x1))
{
i++;
}
// STBY mode
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
// Select the normal memory array
HW_OTPC_REG_SETF(MODE, USE_SP_ROWS, 0);
return i;
}
static bool manual_prog_verify(uint32_t cell_offset, uint32_t pword_l, uint32_t pword_h)
{
uint32_t val_l, val_h, old_mode = OTPC->OTPC_MODE_REG;
uint32_t *addr = hw_otpc_cell_to_mem(cell_offset);
/* we have to go through standby mode first */
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
__DMB();
HW_OTPC_REG_SETF(MODE, ERR_RESP_DIS, 1);
__DMB();
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_MREAD);
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, RERROR))
{
HW_OTPC_REG_SETF(STAT, RERROR,
1); //This bit need to be cleared manually (can only happen if a previous read has not checked/cleared it)
}
/*
* Read cell in manual mode, as 2 32-bit values (little-endian)
*/
val_l = addr[0];
val_h = addr[1];
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
__DMB();
OTPC->OTPC_MODE_REG = old_mode;
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, RERROR))
{
HW_OTPC_REG_SETF(STAT, RERROR, 1); //This bit need to be cleared manually
return false;
}
return (pword_h == val_h) && (pword_l == val_l);
}
/*
* Set to 1 to manually verify the programmed cell value, if the auto-verification fails.
*/
#define MANUAL_PROG_VERIFICATION 1
bool hw_otpc_manual_word_prog(uint32_t cell_offset, uint32_t pword_l, uint32_t pword_h, bool use_rr)
{
int i;
bool ret = true;
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
/*
* Program the data regs
*/
OTPC->OTPC_PWORDL_REG = pword_l;
OTPC->OTPC_PWORDH_REG = pword_h;
/*
* Start programming
*/
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_MPROG);
OTPC->OTPC_PCTRL_REG =
HW_OTPC_FIELD_VAL(PCTRL, WADDR, cell_offset) |
HW_OTPC_FIELD_VAL(PCTRL, PSTART, 1);
wait_for_prog_done();
/*
* Check and retry up to 10 times
*/
i = 0;
while (have_prog_error())
{
if (i == 10)
{
break;
}
i = i + 1;
OTPC->OTPC_PCTRL_REG =
HW_OTPC_FIELD_VAL(PCTRL, WADDR, cell_offset) |
HW_OTPC_FIELD_VAL(PCTRL, PSTART, 1) |
HW_OTPC_FIELD_VAL(PCTRL, PRETRY, 1);
wait_for_prog_done();
}
if (i == 10)
{
ret = false;
if (MANUAL_PROG_VERIFICATION)
{
ret = manual_prog_verify(cell_offset, pword_l, pword_h);
}
if (use_rr && !ret)
{
do
{
// Reset state
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
// Abort if the writing was done in the spare area
if (OTPC->OTPC_MODE_REG & HW_OTPC_REG_FIELD_MASK(MODE, USE_SP_ROWS))
{
break;
}
// Write the repair record to the spare area
if (!hw_otpc_write_rr(cell_offset, pword_l, pword_h))
{
break;
}
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
// force reloading
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_MREAD);
ret = true;
break;
}
while (0);
}
}
HW_OTPC_REG_SETF(MODE, MODE, HW_OTPC_MODE_STBY);
return ret;
}
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)
{
uint32_t w, c, ncells, off, val;
bool ret = true;
const uint32_t *addr;
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
if (num_of_words == 0)
{
return true; // early exit
}
/* index in p_data[] */
w = 0;
off = cell_offset;
if (cell_word == HW_OTPC_WORD_HIGH)
{
/*
* read low 32-bit word so that we program the same value
*/
addr = hw_otpc_cell_to_mem(cell_offset);
hw_otpc_manual_read_on(use_rr);
/* little-endian */
val = addr[0];
hw_otpc_manual_read_off();
if (!hw_otpc_manual_word_prog(off++, val, p_data[w++], use_rr))
{
return false;
}
ncells = (num_of_words - 1) >> 1;
}
else
{
ncells = num_of_words >> 1;
}
for (c = 0; c < ncells; c++, off++, w += 2)
{
if (!hw_otpc_manual_word_prog(off, p_data[w], p_data[w + 1], use_rr))
{
return false;
}
}
if (w < num_of_words)
{
/*
* read high 32-bit word so that we program the same value
*/
addr = hw_otpc_cell_to_mem(off);
hw_otpc_manual_read_on(use_rr);
/* little-endian */
val = addr[1];
hw_otpc_manual_read_off();
if (!hw_otpc_manual_word_prog(off, p_data[w++], val, use_rr))
{
return false;
}
}
ASSERT_WARNING(w == num_of_words);
return ret;
}
bool hw_otpc_write_rr(uint32_t cell_addr, uint32_t pword_l, uint32_t pword_h)
{
uint32_t repair_cnt;
bool ret = false;
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
// Get the number of used Repair Records
repair_cnt = hw_otpc_num_of_rr();
// Abort if all repair records are being used
if (repair_cnt < MAX_RR_AVAIL)
{
do
{
// The access will be performed in the spare rows
HW_OTPC_REG_SETF(MODE, USE_SP_ROWS, 1);
// Write the data to the spare area
if (!hw_otpc_manual_word_prog(0x4F - repair_cnt - 1,
pword_l, pword_h, false))
{
break;
}
// Write the header of the repair record to the spare area
if (!hw_otpc_manual_word_prog(0x4F - repair_cnt,
1 | (cell_addr << 1), 0x00000000, false))
{
break;
}
ret = true;
}
while (0);
// Return to the normal memory array
HW_OTPC_REG_SETF(MODE, USE_SP_ROWS, 0);
if (ret)
{
/*
* Request the reloading of the repair records at the next
* enabling of the OTP cell
*/
HW_OTPC_REG_SETF(MODE, RLD_RR_REQ, 1);
}
}
return ret;
}
void hw_otpc_manual_read_on(bool spare_rows)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
/*
* Place the OTPC in manual read mode
*/
if (spare_rows)
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_MREAD) |
HW_OTPC_FIELD_VAL(MODE, USE_SP_ROWS, 1);
}
else
{
OTPC->OTPC_MODE_REG = HW_OTPC_MODE_MREAD;
}
}
void hw_otpc_manual_read_off(void)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_MREAD);
/*
* Place the OTPC in STBY mode
*/
OTPC->OTPC_MODE_REG = HW_OTPC_MODE_STBY;
}
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)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
/*
* Make sure that the size is valid
*/
ASSERT_WARNING(num_of_words);
ASSERT_WARNING(num_of_words < 16384);
/*
* Set up DMA
*/
unsigned int remap_type = REG_GETF(CRG_TOP, SYS_CTRL_REG, REMAP_ADR0);
if (IS_SYSRAM_ADDRESS(p_data) ||
(IS_REMAPPED_ADDRESS(p_data) && (remap_type == 0x3)))
{
OTPC->OTPC_AHBADR_REG = black_orca_phy_addr((uint32_t)p_data);
#if dg_configEXEC_MODE != MODE_IS_CACHED
}
else if (IS_CACHERAM_ADDRESS(p_data))
{
OTPC->OTPC_AHBADR_REG = black_orca_phy_addr((uint32_t)p_data);
#endif
}
else
{
/*
* Destination address can only reside in RAM or Cache RAM, but in case of remapped
* address, REMAP_ADR0 cannot be 0x6 (Cache Data RAM)
*/
ASSERT_WARNING(0);
}
OTPC->OTPC_CELADR_REG = (cell_offset << 1) | (cell_word == HW_OTPC_WORD_HIGH);
OTPC->OTPC_NWORDS_REG = num_of_words - 1;
/*
* Start DMA programming
*/
if (spare_rows)
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_APROG) |
HW_OTPC_FIELD_VAL(MODE, USE_DMA, 1) |
HW_OTPC_FIELD_VAL(MODE, USE_SP_ROWS, 1);
}
else
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_APROG) |
HW_OTPC_FIELD_VAL(MODE, USE_DMA, 1);
}
wait_for_auto_done();
/*
* Check result
*/
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, PERR_UNC))
{
return false;
}
return true;
}
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)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
/*
* Make sure that the size is valid
*/
ASSERT_WARNING(num_of_words);
ASSERT_WARNING(num_of_words < 16384);
/*
* Set up DMA
*/
unsigned int remap_type = REG_GETF(CRG_TOP, SYS_CTRL_REG, REMAP_ADR0);
if (IS_SYSRAM_ADDRESS(p_data) ||
(IS_REMAPPED_ADDRESS(p_data) && (remap_type == 0x3)))
{
OTPC->OTPC_AHBADR_REG = black_orca_phy_addr((uint32_t)p_data);
#if dg_configEXEC_MODE != MODE_IS_CACHED
}
else if (IS_CACHERAM_ADDRESS(p_data))
{
OTPC->OTPC_AHBADR_REG = black_orca_phy_addr((uint32_t)p_data);
#endif
}
else
{
/*
* Destination address can only reside in RAM or Cache RAM, but in case of remapped
* address, REMAP_ADR0 cannot be 0x6 (Cache Data RAM)
*/
ASSERT_WARNING(0);
}
OTPC->OTPC_CELADR_REG = (cell_offset << 1) | (cell_word == HW_OTPC_WORD_HIGH);
OTPC->OTPC_NWORDS_REG = num_of_words - 1;
/*
* Start DMA programming
*/
if (spare_rows)
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_AREAD) |
HW_OTPC_FIELD_VAL(MODE, USE_DMA, 1) |
HW_OTPC_FIELD_VAL(MODE, USE_SP_ROWS, 1);
}
else
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_AREAD) |
HW_OTPC_FIELD_VAL(MODE, USE_DMA, 1);
}
wait_for_auto_done();
}
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)
{
unsigned int i;
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
ASSERT_WARNING_NONZERO_RANGE(num_of_words, 16384);
/*
* Set up FIFO
*/
OTPC->OTPC_CELADR_REG = (cell_offset << 1) | (cell_word == HW_OTPC_WORD_HIGH);
OTPC->OTPC_NWORDS_REG = num_of_words - 1;
/*
* Perform programming via FIFO
*/
if (spare_rows)
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_APROG) |
HW_OTPC_FIELD_VAL(MODE, USE_SP_ROWS, 1);
}
else
{
OTPC->OTPC_MODE_REG = HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_APROG);
}
for (i = 0; i < num_of_words; i++)
{
while (HW_OTPC_REG_GETF(STAT, FWORDS) == 8)
;
OTPC->OTPC_FFPRT_REG = p_data[i]; // Write FIFO data
}
/*
* Wait for completion
*/
wait_for_auto_done();
/*
* Check result
*/
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, PERR_UNC))
{
return false;
}
return true;
}
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)
{
unsigned int i;
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_CELL_OFFSET_VALID(cell_offset);
ASSERT_WARNING_NONZERO_RANGE(num_of_words, 16384);
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, RERROR))
{
HW_OTPC_REG_SETF(STAT, RERROR,
1); //This bit need to be cleared manually (can only happen if a previous read has not checked/cleared it)
}
/*
* Set up FIFO
*/
OTPC->OTPC_CELADR_REG = (cell_offset << 1) | (cell_word == HW_OTPC_WORD_HIGH);
OTPC->OTPC_NWORDS_REG = num_of_words - 1;
/*
* Perform reading via FIFO
*/
if (spare_rows)
{
OTPC->OTPC_MODE_REG =
HW_OTPC_FIELD_VAL(MODE, MODE, HW_OTPC_MODE_AREAD) |
HW_OTPC_FIELD_VAL(MODE, USE_SP_ROWS, 1);
}
else
{
OTPC->OTPC_MODE_REG = HW_OTPC_MODE_AREAD;
}
for (i = 0; i < num_of_words; i++)
{
while (HW_OTPC_REG_GETF(STAT, FWORDS) == 0)
;
p_data[i] = OTPC->OTPC_FFPRT_REG;
}
/*
* Wait for completion
*/
wait_for_auto_done();
/*
* Check result
*/
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, RERROR))
{
HW_OTPC_REG_SETF(STAT, RERROR, 1); //This bit need to be cleared manually
return false;
}
return true;
}
void hw_otpc_prepare(uint32_t num_of_bytes)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
ASSERT_WARNING_NONZERO_RANGE(num_of_bytes, 65536);
/*
* Set up image size
*/
OTPC->OTPC_NWORDS_REG = ((num_of_bytes + 3) >> 2) - 1;
/*
* Enable OTP_COPY
*/
CRG_TOP->SYS_CTRL_REG |= 1 << REG_POS(CRG_TOP, SYS_CTRL_REG, OTP_COPY);
}
void hw_otpc_cancel_prepare(void)
{
ASSERT_WARNING_OTP_CLK_ENABLED;
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
/*
* Disable OTP_COPY
*/
CRG_TOP->SYS_CTRL_REG &= ~REG_MSK(CRG_TOP, SYS_CTRL_REG, OTP_COPY);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// TEST FUNCTIONALITY
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Execution of the test.
*
* \return The test result.
* <ul>
* <li> 0, if the test succeeded
* <li> 1, if the test 1 failed
* </ul>
*
*/
static int hw_otpc_core_test(int mode)
{
ASSERT_WARNING_OTPC_MODE(HW_OTPC_MODE_STBY);
/*
* Put OTPC in the proper test mode
*/
HW_OTPC_REG_SETF(MODE, MODE, mode);
/*
* Wait end of test
*/
while (!(OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, TRDY)))
;
/*
* Check result
*/
if (OTPC->OTPC_STAT_REG & HW_OTPC_REG_FIELD_MASK(STAT, TERROR))
{
return 1;
}
return 0;
}
int hw_otpc_blank(void)
{
return hw_otpc_core_test(HW_OTPC_MODE_TBLANK);
}
int hw_otpc_tdec(void)
{
return hw_otpc_core_test(HW_OTPC_MODE_TDEC);
}
int hw_otpc_twr(void)
{
return hw_otpc_core_test(HW_OTPC_MODE_TWR);
}
#endif /* dg_configUSE_HW_OTPC */
/**
* \}
* \}
* \}
*/