blob: 3fe280825b5e7130b086b5ee264ba60dcda8e971 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/imxrt/imxrt_ocotp.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <nuttx/clock.h>
#include <arch/board/board.h>
#include "arm_internal.h"
#include "imxrt_periphclks.h"
#include "imxrt_ocotp.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* OCOTP Timing */
#define OCOTP_OPT_TIMEOUT_MS 100
/****************************************************************************
* Private Function
****************************************************************************/
static inline void imxrt_ocotp_reset_errors(void)
{
putreg32(OCOTP_CTRL_ERROR, IMXRT_OCOTP_CTRL_CLR);
}
static void imxrt_ocotp_initialize(void)
{
static bool once = false;
uint32_t read;
uint32_t prog;
uint32_t relax;
uint32_t relax_read;
uint32_t relax_prog;
uint32_t wait;
const uint32_t ipg_freq_hz = BOARD_CPU_FREQUENCY / IMXRT_IPG_PODF_DIVIDER;
if (!once)
{
once = true;
imxrt_clockall_ocotp_ctrl();
/* WAIT specifies time interval between auto read and write
* access in one time program.
*
* Subject to:
* tSP_RD = (WAIT+1)/ipg_clk_freq, should be ≧ 150 ns.
* WAIT = (150ns * ipg_clk_freq) -1
*/
wait = (((uint64_t)(150LL * ipg_freq_hz) + 1000000000LL)
/ 1000000000LL) - 1;
/* RELAX is used to configure the setup/hold time for certain
* timing margin.
*
* Subject to:
* tSP_PGM = tHP_PGM = (RELAX+1)/ipg_clk_freq, should be ≧ 100 ns.
* RELAX = (100ns * ipg_clk_freq) -1
*/
relax = (((uint64_t)(100LL * ipg_freq_hz) + 1000000000LL)
/ 1000000000LL) - 1;
/* RELAX_READ is used to configure the setup/hold time for
* certain timing margin.
*
* Subject to:
* (RELAX_READ+1)/ipg_clk_freq should be ≧ 10 ns.
* RELAX_READ = (10ns * ipg_clk_freq) -1
*/
relax_read = (((uint64_t)(10LL * ipg_freq_hz) + 1000000000LL)
/ 1000000000LL) - 1;
/* RELAX_PROG is used to configure the setup/hold time for
* certain timing margin.
*
* Subject to:
* tSP_PG_AVDD = tHP_PG_AVDD = (RELAX_PROG+1)/ipg_clk_freq,
* should be ≧ 1000 ns.
*
* RELAX_PROG = (1000 ns * ipg_clk_freq) -1
*/
relax_prog = (((uint64_t)(1000LL * ipg_freq_hz) + 1000000000LL)
/ 1000000000LL) - 1;
/* STROBE_PROG configure the program strobe
*
* Subject to:
*
* The tPGM should be configured within the range of 9000 ns <
* tPGM < 11000 ns, while its recommended value is 10000 ns.
*
* tPGM = [(STROBE_PROG+1) – 2×(RELAX_PROG+1)]/ipg_clk_freq.
* 10000 = [(STROBE_PROG+1) – 2×(RELAX_PROG+1)]/ipg_clk_freq.
* STROBE_PROG = (10000 * ipg_clk_freq) + 2×(RELAX_PROG+1)) - 1
*
*/
prog = (((uint64_t)(10000LL * ipg_freq_hz) + 1000000000LL) /
1000000000LL + 2 * (relax_prog + 1)) - 1;
/* STROBE_READ configure the program strobe
*
* Subject to:
*
* The tRD is required to be larger than 40 ns.
*
* tRD = [(STROBE_READ+1) – 2×(RELAX_READ+1)]/ipg_clk_freq.
* 40 = [(STROBE_READ+1) – 2×(RELAX_READ+1)]/ipg_clk_freq.
* STROBE_READ = (40 * ipg_clk_freq) + 2×(RELAX_READ+1)) - 1
*
*/
read = (((uint64_t)(40LL * ipg_freq_hz) + 1000000000LL) /
1000000000LL + 2 * (relax_read + 1)) - 1;
modifyreg32(IMXRT_OCOTP_TIMING,
OCOTP_TIMING_WAIT_MASK | OCOTP_TIMING_STROBE_READ_MASK |
OCOTP_TIMING_RELAX_MASK | OCOTP_TIMING_STROBE_PROG_MASK,
OCOTP_TIMING_WAIT(wait) | OCOTP_TIMING_STROBE_READ(read) |
OCOTP_TIMING_RELAX(relax) | OCOTP_TIMING_STROBE_PROG(prog));
modifyreg32(IMXRT_OCOTP_TIMING2,
OCOTP_TIMING2_RELAX_READ_MASK |
OCOTP_TIMING2_RELAX_PROG_MASK,
OCOTP_TIMING2_RELAX_READ(relax_read) |
OCOTP_TIMING2_RELAX_PROG(relax_prog));
}
}
static int imxrt_ocotp_wait_for_completion(uint32_t timeout_ms)
{
clock_t timeout;
clock_t start;
/* Get the timeout value */
timeout = MSEC2TICK(timeout_ms);
start = clock_systime_ticks();
while (getreg32(IMXRT_OCOTP_CTRL) & OCOTP_CTRL_BUSY)
{
/* If a timeout is specified check for timeout */
if (timeout_ms && clock_systime_ticks() - start >= timeout)
{
return -ETIME;
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: imxrt_ocotp_reload
*
* Description:
* Reload the Shadow from OTP.
*
****************************************************************************/
int imxrt_ocotp_reload()
{
int ret;
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
if (ret == OK)
{
imxrt_ocotp_reset_errors();
imxrt_ocotp_initialize();
putreg32(OCOTP_CTRL_RELOAD_SHADOWS, IMXRT_OCOTP_CTRL_SET);
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
}
return ret;
}
/****************************************************************************
* Name: imxrt_ocotp_read
*
* Description:
* Read one value from the OTP.
*
* Input Parameters:
* otp_index - otp index (0-63)
* data - a pointer to store the retrieved data
*
* Returned Value
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int imxrt_ocotp_read(uint32_t otp_index, uint32_t *data)
{
int ret;
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
if (ret == OK)
{
imxrt_ocotp_reset_errors();
imxrt_ocotp_initialize();
putreg32(OCOTP_CTRL_ADDR_MASK, IMXRT_OCOTP_CTRL_CLR);
putreg32(OCOTP_CTRL_ADDR(otp_index), IMXRT_OCOTP_CTRL_SET);
putreg32(OCOTP_READ_CTRL_READ_FUSE, IMXRT_OCOTP_READ_CTRL);
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
if (ret == OK)
{
if ((getreg32(IMXRT_OCOTP_CTRL) & OCOTP_CTRL_ERROR) != 0)
{
ret = -EIO;
}
else
{
*data = getreg32(IMXRT_OCOTP_READ_FUSE_DATA);
ret = 0;
}
}
}
return ret;
}
/****************************************************************************
* Name: imxrt_ocotp_write
*
* Description:
* Write one value to the OTP.
*
* Input Parameters:
* otp_index - otp index (0-63)
* data - data to write to OTP
*
* Returned Value
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
*
****************************************************************************/
int imxrt_ocotp_write(uint32_t otp_index, uint32_t data)
{
int ret;
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
if (ret == OK)
{
imxrt_ocotp_reset_errors();
imxrt_ocotp_initialize();
putreg32(OCOTP_CTRL_ADDR_MASK | OCOTP_CTRL_WR_UNLOCK_MASK,
IMXRT_OCOTP_CTRL_CLR);
putreg32(OCOTP_CTRL_ADDR(otp_index) | OCOTP_CTRL_WR_UNLOCK,
IMXRT_OCOTP_CTRL_SET);
putreg32(data, IMXRT_OCOTP_DATA);
ret = imxrt_ocotp_wait_for_completion(OCOTP_OPT_TIMEOUT_MS);
if (ret == OK)
{
if ((getreg32(IMXRT_OCOTP_CTRL) & OCOTP_CTRL_ERROR) != 0)
{
ret = -EIO;
}
else
{
ret = imxrt_ocotp_reload();
}
}
}
return ret;
}