Files
rt-thread/bsp/rockchip/dm/nvmem/nvmem-rockchip-otp.c
T
GuEe-GUI d65076d83d [bsp][rockchip] support RK3528
1. CLK for RK3528
2. Pinctrl for RK3528
3. hwrng for RK-RNG
4. OTP for RK3528
5. TSADC for RK3528

Signed-off-by: GuEe-GUI <2991707448@qq.com>
2026-03-31 10:36:42 +08:00

1045 lines
28 KiB
C
Executable File

/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <bitmap.h>
#define DBG_TAG "nvmem.rockchip.otp"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* OTP Register Offsets */
#define OTPC_SBPI_CTRL 0x0020
#define OTPC_SBPI_CMD_VALID_PRE 0x0024
#define OTPC_SBPI_CS_VALID_PRE 0x0028
#define OTPC_SBPI_STATUS 0x002C
#define OTPC_USER_CTRL 0x0100
#define OTPC_USER_ADDR 0x0104
#define OTPC_USER_ENABLE 0x0108
#define OTPC_USER_QP 0x0120
#define OTPC_USER_Q 0x0124
#define OTPC_INT_STATUS 0x0304
#define OTPC_SBPI_CMD0_OFFSET 0x1000
#define OTPC_SBPI_CMD1_OFFSET 0x1004
#define OTPC_MODE_CTRL 0x2000
#define OTPC_IRQ_ST 0x2008
#define OTPC_ACCESS_ADDR 0x200c
#define OTPC_RD_DATA 0x2010
#define OTPC_REPR_RD_TRANS_NUM 0x2020
#define OTPC_DEEP_STANDBY 0x0
#define OTPC_STANDBY 0x1
#define OTPC_ACTIVE 0x2
#define OTPC_READ_ACCESS 0x3
#define OTPC_TRANS_NUM 0x1
#define OTPC_RDM_IRQ_ST RT_BIT(0)
#define OTPC_STB2ACT_IRQ_ST RT_BIT(7)
#define OTPC_DP2STB_IRQ_ST RT_BIT(8)
#define OTPC_ACT2STB_IRQ_ST RT_BIT(9)
#define OTPC_STB2DP_IRQ_ST RT_BIT(10)
#define PX30S_NBYTES 4
#define PX30S_NO_SECURE_OFFSET 224
/* OTP Register bits and masks */
#define OTPC_USER_ADDR_MASK RT_GENMASK(31, 16)
#define OTPC_USE_USER RT_BIT(0)
#define OTPC_USE_USER_MASK RT_GENMASK(16, 16)
#define OTPC_USER_FSM_ENABLE RT_BIT(0)
#define OTPC_USER_FSM_ENABLE_MASK RT_GENMASK(16, 16)
#define OTPC_LOCK RT_BIT(0)
#define OTPC_LOCK_MASK RT_GENMASK(16, 16)
#define OTPC_SBPI_DONE RT_BIT(1)
#define OTPC_USER_DONE RT_BIT(2)
#define SBPI_DAP_ADDR 0x02
#define SBPI_DAP_ADDR_SHIFT 8
#define SBPI_DAP_ADDR_MASK RT_GENMASK(31, 24)
#define SBPI_CMD_VALID_MASK RT_GENMASK(31, 16)
#define SBPI_DAP_CMD_WRF 0xc0
#define SBPI_DAP_REG_ECC 0x3a
#define SBPI_ECC_ENABLE 0x00
#define SBPI_ECC_DISABLE 0x09
#define SBPI_ENABLE RT_BIT(0)
#define SBPI_ENABLE_MASK RT_GENMASK(16, 16)
#define OTPC_TIMEOUT 10000
#define OTPC_TIMEOUT_PROG 100000
#define RK3568_NBYTES 2
#define RK3576_NO_SECURE_OFFSET 0x1c0
#define RK3588_OTPC_AUTO_CTRL 0x04
#define RK3588_OTPC_AUTO_EN 0x08
#define RK3588_OTPC_INT_ST 0x84
#define RK3588_OTPC_DOUT0 0x20
#define RK3588_NO_SECURE_OFFSET 0x300
#define RK3588_NBYTES 4
#define RK3588_BURST_NUM 1
#define RK3588_BURST_SHIFT 8
#define RK3588_ADDR_SHIFT 16
#define RK3588_AUTO_EN RT_BIT(0)
#define RK3588_RD_DONE RT_BIT(1)
#define RV1126_OTP_NVM_CEB 0x00
#define RV1126_OTP_NVM_RSTB 0x04
#define RV1126_OTP_NVM_ST 0x18
#define RV1126_OTP_NVM_RADDR 0x1c
#define RV1126_OTP_NVM_RSTART 0x20
#define RV1126_OTP_NVM_RDATA 0x24
#define RV1126_OTP_NVM_TRWH 0x28
#define RV1126_OTP_READ_ST 0x30
#define RV1126_OTP_NVM_PRADDR 0x34
#define RV1126_OTP_NVM_PRLEN 0x38
#define RV1126_OTP_NVM_PRDATA 0x3c
#define RV1126_OTP_NVM_FAILTIME 0x40
#define RV1126_OTP_NVM_PRSTART 0x44
#define RV1126_OTP_NVM_PRSTATE 0x48
/*
* +----------+------------------+--------------------------+
* | TYPE | RANGE(byte) | NOTE |
* +----------+------------------+--------------------------+
* | system | 0x000 ~ 0x0ff | system info, read only |
* +----------+------------------+--------------------------+
* | oem | 0x100 ~ 0x1ef | for customized |
* +----------+------------------+--------------------------+
* | reserved | 0x1f0 ~ 0x1f7 | future extension |
* +----------+------------------+--------------------------+
* | wp | 0x1f8 ~ 0x1ff | write protection for oem |
* +----------+------------------+--------------------------+
*
* +-----+ +------------------+
* | wp | -- | wp for oem range |
* +-----+ +------------------+
* | 1f8 | | 0x100 ~ 0x11f |
* +-----+ +------------------+
* | 1f9 | | 0x120 ~ 0x13f |
* +-----+ +------------------+
* | 1fa | | 0x140 ~ 0x15f |
* +-----+ +------------------+
* | 1fb | | 0x160 ~ 0x17f |
* +-----+ +------------------+
* | 1fc | | 0x180 ~ 0x19f |
* +-----+ +------------------+
* | 1fd | | 0x1a0 ~ 0x1bf |
* +-----+ +------------------+
* | 1fe | | 0x1c0 ~ 0x1df |
* +-----+ +------------------+
* | 1ff | | 0x1e0 ~ 0x1ef |
* +-----+ +------------------+
*/
#define RV1126_OTP_OEM_OFFSET 0x100
#define RV1126_OTP_OEM_SIZE 0xf0
#define RV1126_OTP_WP_OFFSET 0x1f8
#define RV1126_OTP_WP_SIZE 0x8
/* each bit mask 32 bits in OTP NVM */
#define ROCKCHIP_OTP_WP_MASK_NBITS 64
#define OFFSET_IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
struct rockchip_otp;
struct rockchip_otp_data
{
int size;
int ns_offset;
rt_ssize_t (*read)(struct rockchip_otp *, int offset, void *buf, rt_size_t bytes);
rt_ssize_t (*write)(struct rockchip_otp *, int offset, void *buf, rt_size_t bytes);
rt_err_t (*init)(struct rockchip_otp *);
};
struct rockchip_otp
{
struct rt_nvmem_device parent;
void *regs;
rt_bool_t was_written;
struct rt_clk_array *clk_arr;
struct rt_reset_control *rstc;
const struct rockchip_otp_data *soc_data;
struct rt_mutex mutex;
RT_BITMAP_DECLARE(wp_mask, ROCKCHIP_OTP_WP_MASK_NBITS);
};
#define raw_to_rockchip_otp(raw) rt_container_of(raw, struct rockchip_otp, parent)
#define readl_poll_timeout(ADDR, VAL, COND, DELAY_US, TIMEOUT_US) \
({ \
rt_uint64_t timeout_us = (TIMEOUT_US); \
rt_int64_t left_ns = timeout_us * 1000L; \
rt_ubase_t delay_us = (DELAY_US); \
rt_uint64_t delay_ns = delay_us * 1000L; \
for (;;) \
{ \
(VAL) = HWREG32(ADDR); \
if (COND) \
{ \
break; \
} \
if (timeout_us && left_ns < 0) \
{ \
(VAL) = HWREG32(ADDR); \
break; \
} \
if (delay_us) \
{ \
rt_hw_us_delay(delay_us); \
if (timeout_us) \
{ \
left_ns -= delay_ns; \
} \
} \
rt_hw_cpu_relax(); \
if (timeout_us) \
{ \
--left_ns; \
} \
} \
(COND) ? RT_EOK : -RT_ETIMEOUT; \
})
static rt_err_t rockchip_otp_reset(struct rockchip_otp *rk_otp)
{
rt_err_t err;
if ((err = rt_reset_control_assert(rk_otp->rstc)))
{
LOG_E("Failed to assert otp phy error = %s", rt_strerror(err));
return err;
}
rt_hw_us_delay(2);
if ((err = rt_reset_control_deassert(rk_otp->rstc)))
{
LOG_E("Failed to deassert otp phy error = %s", rt_strerror(err));
return err;
}
return RT_EOK;
}
static rt_err_t px30_otp_wait_status(struct rockchip_otp *rk_otp, rt_uint32_t flag)
{
rt_err_t err;
rt_uint32_t status = 0;
err = readl_poll_timeout(rk_otp->regs + OTPC_INT_STATUS, status,
(status & flag), 1, OTPC_TIMEOUT);
if (err)
{
return err;
}
/* Clean int status */
HWREG32(rk_otp->regs + OTPC_INT_STATUS) = flag;
return RT_EOK;
}
static rt_err_t px30_otp_ecc_enable(struct rockchip_otp *rk_otp, rt_bool_t enable)
{
rt_err_t err;
HWREG32(rk_otp->regs + OTPC_SBPI_CTRL) = SBPI_DAP_ADDR_MASK |
(SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT);
HWREG32(rk_otp->regs + OTPC_SBPI_CMD_VALID_PRE) = SBPI_CMD_VALID_MASK | 0x1;
HWREG32(rk_otp->regs + OTPC_SBPI_CMD0_OFFSET) =
SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC;
HWREG32(rk_otp->regs + OTPC_SBPI_CMD1_OFFSET) =
enable ? SBPI_ECC_ENABLE : SBPI_ECC_DISABLE;
HWREG32(rk_otp->regs + OTPC_SBPI_CTRL) = SBPI_ENABLE_MASK | SBPI_ENABLE;
if ((err = px30_otp_wait_status(rk_otp, OTPC_SBPI_DONE)) < 0)
{
LOG_E("Timeout during ecc_enable");
}
return err;
}
static rt_ssize_t px30_otp_read(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
rt_uint8_t *buf = val;
if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0)
{
LOG_E("Failed to prepare/enable clks");
return res;
}
if ((res = rockchip_otp_reset(rk_otp)))
{
LOG_E("Failed to reset otp phy");
goto _disable_clks;
}
if ((res = px30_otp_ecc_enable(rk_otp, RT_FALSE)) < 0)
{
LOG_E("Enable ECC error = %s", rt_strerror(res));
goto _disable_clks;
}
HWREG32(rk_otp->regs + OTPC_USER_CTRL) = OTPC_USE_USER | OTPC_USE_USER_MASK;
rt_hw_us_delay(5);
for (int i = 0; i < bytes; ++i)
{
HWREG32(rk_otp->regs + OTPC_USER_ADDR) = offset++ | OTPC_USER_ADDR_MASK;
HWREG32(rk_otp->regs + OTPC_USER_ENABLE) =
OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK;
if ((res = px30_otp_wait_status(rk_otp, OTPC_USER_DONE)) < 0)
{
LOG_E("Timeout during read setup");
goto _read_end;
}
*buf++ = HWREG8(rk_otp->regs + OTPC_USER_Q);
}
_read_end:
HWREG32(rk_otp->regs + OTPC_USER_CTRL) = 0x0 | OTPC_USE_USER_MASK;
_disable_clks:
rt_clk_array_disable_unprepare(rk_otp->clk_arr);
return bytes;
}
static rt_err_t px30s_otp_wait_status(struct rockchip_otp *rk_otp, rt_uint32_t flag)
{
rt_err_t err;
rt_uint32_t status = 0;
err = readl_poll_timeout(rk_otp->regs + OTPC_IRQ_ST, status,
(status & flag), 1, OTPC_TIMEOUT);
if (err)
{
return err;
}
/* clean int status */
HWREG32(rk_otp->regs + OTPC_IRQ_ST) = flag;
return RT_EOK;
}
static rt_err_t px30s_otp_active(struct rockchip_otp *rk_otp)
{
rt_uint32_t mode;
rt_err_t err = RT_EOK;
mode = HWREG32(rk_otp->regs + OTPC_MODE_CTRL);
switch (mode)
{
case OTPC_DEEP_STANDBY:
HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_STANDBY;
if ((err = px30s_otp_wait_status(rk_otp, OTPC_DP2STB_IRQ_ST)) < 0)
{
LOG_E("Timeout during wait dp2stb");
return err;
}
/* Fallthrough */
case OTPC_STANDBY:
HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_ACTIVE;
if ((err = px30s_otp_wait_status(rk_otp, OTPC_STB2ACT_IRQ_ST)) < 0)
{
LOG_E("Timeout during wait stb2act");
return err;
}
break;
default:
break;
}
return err;
}
static rt_err_t px30s_otp_standby(struct rockchip_otp *rk_otp)
{
rt_uint32_t mode;
rt_err_t err = RT_EOK;
mode = HWREG32(rk_otp->regs + OTPC_MODE_CTRL);
switch (mode)
{
case OTPC_ACTIVE:
HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_STANDBY;
if ((err = px30s_otp_wait_status(rk_otp, OTPC_ACT2STB_IRQ_ST)) < 0)
{
LOG_E("Timeout during wait act2stb");
return err;
}
/* Fallthrough */
case OTPC_STANDBY:
HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_DEEP_STANDBY;
if ((err = px30s_otp_wait_status(rk_otp, OTPC_STB2DP_IRQ_ST)) < 0)
{
LOG_E("Timeout during wait stb2dp");
return err;
}
break;
default:
break;
}
return err;
}
static rt_ssize_t px30s_otp_read(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
rt_uint8_t *buf;
rt_uint32_t out_value;
int addr_start, addr_end, addr_offset, addr_len;
if (offset >= rk_otp->soc_data->size)
{
return -RT_ENOMEM;
}
if (offset + bytes > rk_otp->soc_data->size)
{
bytes = rk_otp->soc_data->size - offset;
}
if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0)
{
LOG_E("Failed to prepare/enable clks");
return res;
}
if ((res = rockchip_otp_reset(rk_otp)))
{
LOG_E("Failed to reset otp phy");
goto _disable_clks;
}
if ((res = px30s_otp_active(rk_otp)))
{
goto _disable_clks;
}
addr_start = rt_rounddown(offset, PX30S_NBYTES) / PX30S_NBYTES;
addr_end = rt_roundup(offset + bytes, PX30S_NBYTES) / PX30S_NBYTES;
addr_offset = offset % PX30S_NBYTES;
addr_len = addr_end - addr_start;
addr_start += PX30S_NO_SECURE_OFFSET;
buf = rt_calloc(PX30S_NBYTES, addr_len * sizeof(*buf));
if (!buf)
{
res = -RT_ENOMEM;
goto _read_end;
}
for (int i = 0; addr_len--; i += PX30S_NBYTES)
{
HWREG32(rk_otp->regs + OTPC_REPR_RD_TRANS_NUM) = OTPC_TRANS_NUM;
HWREG32(rk_otp->regs + OTPC_ACCESS_ADDR) = addr_start++;
HWREG32(rk_otp->regs + OTPC_MODE_CTRL) = OTPC_READ_ACCESS;
if ((res = px30s_otp_wait_status(rk_otp, OTPC_RDM_IRQ_ST)) < 0)
{
LOG_E("timeout during wait rd");
goto _read_end;
}
out_value = HWREG32(rk_otp->regs + OTPC_RD_DATA);
rt_memcpy(&buf[i], &out_value, PX30S_NBYTES);
}
rt_memcpy(val, buf + addr_offset, (rt_uint32_t)bytes);
res = bytes;
_read_end:
rt_free(buf);
px30s_otp_standby(rk_otp);
_disable_clks:
rt_clk_array_disable_unprepare(rk_otp->clk_arr);
return res;
}
static rt_ssize_t rk3568_otp_read(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
rt_uint8_t *buf;
rt_uint32_t otp_qp, out_value;
int addr_start, addr_end, addr_offset, addr_len;
addr_start = rt_rounddown(offset, RK3568_NBYTES) / RK3568_NBYTES;
addr_end = rt_roundup(offset + bytes, RK3568_NBYTES) / RK3568_NBYTES;
addr_offset = offset % RK3568_NBYTES;
addr_len = addr_end - addr_start;
buf = rt_calloc(RK3568_NBYTES, addr_len * sizeof(*buf));
if (!buf)
{
return -RT_ENOMEM;
}
if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0)
{
LOG_E("Failed to prepare/enable clks");
goto _out_free;
}
if ((res = rockchip_otp_reset(rk_otp)))
{
LOG_E("failed to reset otp phy");
goto _disable_clks;
}
if ((res = px30_otp_ecc_enable(rk_otp, RT_TRUE)) < 0)
{
LOG_E("Enable ECC error = %s", rt_strerror(res));
goto _disable_clks;
}
HWREG32(rk_otp->regs + OTPC_USER_CTRL) = OTPC_USE_USER | OTPC_USE_USER_MASK;
rt_hw_us_delay(5);
for (int i = 0; addr_len--; i += RK3568_NBYTES)
{
HWREG32(rk_otp->regs + OTPC_USER_ADDR) =
addr_start++ | OTPC_USER_ADDR_MASK;
HWREG32(rk_otp->regs + OTPC_USER_ENABLE) =
OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK;
if ((res = px30_otp_wait_status(rk_otp, OTPC_USER_DONE)) < 0)
{
LOG_E("Timeout during read setup");
goto _read_end;
}
otp_qp = HWREG32(rk_otp->regs + OTPC_USER_QP);
if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20))
{
res = -RT_EIO;
LOG_E("ECC check error during read setup");
goto _read_end;
}
out_value = HWREG32(rk_otp->regs + OTPC_USER_Q);
rt_memcpy(&buf[i], &out_value, RK3568_NBYTES);
}
rt_memcpy(val, buf + addr_offset, bytes);
res = bytes;
_read_end:
HWREG32(rk_otp->regs + OTPC_USER_CTRL) = 0x0 | OTPC_USE_USER_MASK;
_disable_clks:
rt_clk_array_disable_unprepare(rk_otp->clk_arr);
_out_free:
rt_free(buf);
return res;
}
static rt_err_t rk3588_otp_wait_status(struct rockchip_otp *rk_otp,
rt_uint32_t flag)
{
rt_err_t err;
rt_uint32_t status = 0;
err = readl_poll_timeout(rk_otp->regs + RK3588_OTPC_INT_ST, status,
(status & flag), 1, OTPC_TIMEOUT);
if (err)
{
return err;
}
/* clean int status */
HWREG32(rk_otp->regs + RK3588_OTPC_INT_ST) = flag;
return RT_EOK;
}
static rt_ssize_t rk3588_otp_read(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res = 0;
rt_uint8_t *buf;
rt_uint32_t out_value;
int addr_start, addr_end, addr_offset, addr_len;
if (offset >= rk_otp->soc_data->size)
{
return -RT_ENOMEM;
}
if (offset + bytes > rk_otp->soc_data->size)
{
bytes = rk_otp->soc_data->size - offset;
}
addr_start = rt_rounddown(offset, RK3588_NBYTES) / RK3588_NBYTES;
addr_end = rt_roundup(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES;
addr_offset = offset % RK3588_NBYTES;
addr_len = addr_end - addr_start;
addr_start += rk_otp->soc_data->ns_offset;
buf = rt_calloc(RK3588_NBYTES, addr_len * sizeof(*buf));
if (!buf)
{
return -RT_ENOMEM;
}
if ((res = rt_clk_array_prepare_enable(rk_otp->clk_arr)) < 0)
{
LOG_E("Failed to prepare/enable clks");
goto _out_free;
}
for (int i = 0; addr_len--; i += RK3588_NBYTES, ++addr_start)
{
HWREG32(rk_otp->regs + RK3588_OTPC_AUTO_CTRL) =
(addr_start << RK3588_ADDR_SHIFT) |
(RK3588_BURST_NUM << RK3588_BURST_SHIFT);
HWREG32(rk_otp->regs + RK3588_OTPC_AUTO_EN) = RK3588_AUTO_EN;
if ((res = rk3588_otp_wait_status(rk_otp, RK3588_RD_DONE)) < 0)
{
LOG_E("Timeout during read setup");
goto _read_end;
}
out_value = HWREG32(rk_otp->regs + RK3588_OTPC_DOUT0);
rt_memcpy(&buf[i], &out_value, RK3588_NBYTES);
}
rt_memcpy(val, buf + addr_offset, bytes);
res = bytes;
_read_end:
rt_clk_array_disable_unprepare(rk_otp->clk_arr);
_out_free:
rt_free(buf);
return res;
}
static rt_err_t rv1126_otp_init(struct rockchip_otp *rk_otp)
{
rt_err_t err;
rt_uint32_t status = 0;
HWREG32(rk_otp->regs + RV1126_OTP_NVM_CEB) = 0x0;
err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_ST, status,
status & 0x1, 1, OTPC_TIMEOUT);
if (err < 0)
{
LOG_E("Timeout during set ceb");
return err;
}
HWREG32(rk_otp->regs + RV1126_OTP_NVM_RSTB) = 0x1;
err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_ST, status,
status & 0x4, 1, OTPC_TIMEOUT);
if (err < 0)
{
LOG_E("Timeout during set rstb");
return err;
}
return RT_EOK;
}
static rt_ssize_t rv1126_otp_read(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
rt_uint8_t *buf = val;
rt_uint32_t status = 0;
for (int i = 0; i < bytes; ++i)
{
HWREG32(rk_otp->regs + RV1126_OTP_NVM_RADDR) = offset++;
HWREG32(rk_otp->regs + RV1126_OTP_NVM_RSTART) = 0x1;
res = readl_poll_timeout(rk_otp->regs + RV1126_OTP_READ_ST,
status, status == 0, 1, OTPC_TIMEOUT);
if (res < 0)
{
LOG_E("Timeout during read setup");
return res;
}
*buf++ = HWREG8(rk_otp->regs + RV1126_OTP_NVM_RDATA);
}
return bytes;
}
static rt_err_t rv1126_otp_prog(struct rockchip_otp *rk_otp,
rt_uint32_t bit_offset, rt_uint32_t data, rt_uint32_t bit_len)
{
rt_err_t err;
rt_uint32_t status = 0;
if (!data)
{
return RT_EOK;
}
HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRADDR) = bit_offset;
HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRLEN) = bit_len - 1;
HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRDATA) = data;
HWREG32(rk_otp->regs + RV1126_OTP_NVM_PRSTART) = 1;
/* Wait max 100 ms */
err = readl_poll_timeout(rk_otp->regs + RV1126_OTP_NVM_PRSTATE,
status, status == 0, 1, OTPC_TIMEOUT_PROG);
if (err)
{
LOG_E("Timeout during prog");
}
return err;
}
static rt_ssize_t rv1126_otp_write(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
rt_uint8_t *buf = val, val_r, val_w;
for (int i = 0; i < bytes; ++i)
{
if ((res = rv1126_otp_read(rk_otp, offset, &val_r, 1)))
{
return res;
}
val_w = *buf & (~val_r);
if ((res = rv1126_otp_prog(rk_otp, offset * 8, val_w, 8)))
{
return res;
}
++buf;
++offset;
}
return bytes;
}
static rt_ssize_t rv1126_otp_wp(struct rockchip_otp *rk_otp, int offset,
rt_size_t bytes)
{
int nbits = bytes / 4;
rt_uint32_t bit_idx = (offset - RV1126_OTP_OEM_OFFSET) / 4;
for (int i = 0; i < nbits; ++i)
{
rt_bitmap_set_bit(rk_otp->wp_mask, bit_idx + i);
}
return rv1126_otp_write(rk_otp, RV1126_OTP_WP_OFFSET, rk_otp->wp_mask,
RV1126_OTP_WP_SIZE);
}
static rt_ssize_t rv1126_otp_oem_write(struct rockchip_otp *rk_otp, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res;
if (offset < RV1126_OTP_OEM_OFFSET ||
offset > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE - 1) ||
bytes > RV1126_OTP_OEM_SIZE ||
(offset + bytes) > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE))
{
return -RT_EINVAL;
}
if (!OFFSET_IS_ALIGNED(offset, 4) || !OFFSET_IS_ALIGNED(bytes, 4))
{
return -RT_EINVAL;
}
if (!(res = rv1126_otp_write(rk_otp, offset, val, bytes)))
{
res = rv1126_otp_wp(rk_otp, offset, bytes);
}
return res;
}
static rt_ssize_t rockchip_otp_read(struct rt_nvmem_device *ndev, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res = -RT_EINVAL;
struct rockchip_otp *rk_otp = raw_to_rockchip_otp(ndev);
rt_mutex_take(&rk_otp->mutex, RT_WAITING_FOREVER);
if (rk_otp->soc_data && rk_otp->soc_data->read)
{
res = rk_otp->soc_data->read(rk_otp, offset, val, bytes);
}
rt_mutex_release(&rk_otp->mutex);
return res;
}
static rt_ssize_t rockchip_otp_write(struct rt_nvmem_device *ndev, int offset,
void *val, rt_size_t bytes)
{
rt_ssize_t res = -RT_EINVAL;
struct rockchip_otp *rk_otp = raw_to_rockchip_otp(ndev);
rt_mutex_take(&rk_otp->mutex, RT_WAITING_FOREVER);
if (!rk_otp->was_written && rk_otp->soc_data && rk_otp->soc_data->write)
{
res = rk_otp->soc_data->write(rk_otp, offset, val, bytes);
}
rt_mutex_release(&rk_otp->mutex);
return res;
}
static const struct rockchip_otp_data px30_data =
{
.size = 0x40,
.read = px30_otp_read,
};
static const struct rockchip_otp_data px30s_data =
{
.size = 0x80,
.read = px30s_otp_read,
};
static const struct rockchip_otp_data rk3528_data =
{
.size = 0x80,
.read = rk3568_otp_read,
};
static const struct rockchip_otp_data rk3568_data =
{
.size = 0x80,
.read = rk3568_otp_read,
};
static const struct rockchip_otp_data rk3576_data =
{
.size = 0x100,
.ns_offset = RK3576_NO_SECURE_OFFSET,
.read = rk3588_otp_read,
};
static const struct rockchip_otp_data rk3588_data =
{
.size = 0x400,
.ns_offset = RK3588_NO_SECURE_OFFSET,
.read = rk3588_otp_read,
};
static const struct rockchip_otp_data rv1106_data =
{
.size = 0x80,
.read = rk3568_otp_read,
};
static const struct rockchip_otp_data rv1126_data =
{
.size = 0x200,
.init = rv1126_otp_init,
.read = rv1126_otp_read,
.write = rv1126_otp_oem_write,
};
static rt_err_t rockchip_otp_probe(struct rt_platform_device *pdev)
{
rt_err_t err;
struct rt_nvmem_device *nvmem;
struct rt_device *dev = &pdev->parent;
const struct rockchip_otp_data *soc_data = pdev->id->data;
struct rockchip_otp *rk_otp = rt_calloc(1, sizeof(*rk_otp));
if (!rk_otp)
{
return -RT_ENOMEM;
}
rk_otp->regs = rt_dm_dev_iomap(dev, 0);
if (!rk_otp->regs)
{
err = -RT_EIO;
goto _fail;
}
rk_otp->clk_arr = rt_clk_get_array(dev);
if (rt_is_err(rk_otp->clk_arr))
{
err = rt_ptr_err(rk_otp->clk_arr);
goto _fail;
}
rk_otp->rstc = rt_reset_control_get_array(dev);
if (rt_is_err(rk_otp->rstc))
{
err = rt_ptr_err(rk_otp->rstc);
goto _fail;
}
if (soc_data->init)
{
if ((err = soc_data->init(rk_otp)))
{
goto _fail;
}
}
dev->user_data = rk_otp;
rk_otp->soc_data = soc_data;
nvmem = &rk_otp->parent;
nvmem->parent.ofw_node = dev->ofw_node;
nvmem->reg_read = rockchip_otp_read,
nvmem->reg_write = rockchip_otp_write,
nvmem->size = soc_data->size;
nvmem->read_only = soc_data->write == RT_NULL;
nvmem->stride = 1;
nvmem->word_size = 1;
if ((err = rt_nvmem_device_register(nvmem)))
{
goto _fail;
}
rt_mutex_init(&rk_otp->mutex, "rockchip-otp", RT_IPC_FLAG_PRIO);
return RT_EOK;
_fail:
if (rk_otp->regs)
{
rt_iounmap(rk_otp->regs);
}
if (!rt_is_err_or_null(rk_otp->clk_arr))
{
rt_clk_array_put(rk_otp->clk_arr);
}
rt_free(rk_otp);
return err;
}
static rt_err_t rockchip_otp_remove(struct rt_platform_device *pdev)
{
rt_err_t err;
struct rockchip_otp *rk_otp = pdev->parent.user_data;
err = rt_nvmem_device_unregister(&rk_otp->parent);
rt_iounmap(rk_otp->regs);
rt_clk_array_put(rk_otp->clk_arr);
rt_free(rk_otp);
return err;
}
static const struct rt_ofw_node_id rockchip_otp_ofw_ids[] =
{
{ .compatible = "rockchip,px30-otp", .data = &px30_data, },
{ .compatible = "rockchip,px30s-otp", .data = &px30s_data, },
{ .compatible = "rockchip,rk3308-otp", .data = &px30_data, },
{ .compatible = "rockchip,rk3308bs-otp", .data = &px30s_data, },
{ .compatible = "rockchip,rk3528-otp", .data = &rk3528_data, },
{ .compatible = "rockchip,rk3568-otp", .data = &rk3568_data, },
{ .compatible = "rockchip,rk3576-otp", .data = &rk3576_data, },
{ .compatible = "rockchip,rk3588-otp", .data = &rk3588_data, },
{ .compatible = "rockchip,rv1106-otp", .data = &rv1106_data, },
{ .compatible = "rockchip,rv1126-otp", .data = &rv1126_data, },
{ /* sentinel */ }
};
static struct rt_platform_driver rockchip_otp_driver =
{
.name = "nvmem-rockchip-otp",
.ids = rockchip_otp_ofw_ids,
.probe = rockchip_otp_probe,
.remove = rockchip_otp_remove,
};
RT_PLATFORM_DRIVER_EXPORT(rockchip_otp_driver);