nvmem: rockchip-otp: Add support for oem zone write

The oem zone ranges from 256 to 511 bytes. userspace
can read/write the raw NVMEM file located at
/sys/bus/nvmem/devices/rockchip-otp0/nvmem

The rest of otp which ranging from 0 to 255 bytes is
used for system, it is protected by hardware, any writes
to this range will be ignored and not take effect.

e.g.

/#hexdump -C /sys/bus/nvmem/devices/rockchip-otp0/nvmem
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
*
00000100  ff ff ff ff ff ff ff ff  0f 0f 0f 0f 0f 0f 0f 0f
00000110  00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
*
00000200

Change-Id: I3e222d87525887fd5a38aa724e97f2dd163345aa
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
Sugar Zhang
2020-09-04 21:54:40 +08:00
committed by Tao Huang
parent 4912677140
commit e34822842c

View File

@@ -53,6 +53,7 @@
#define SBPI_ENABLE_MASK GENMASK(16, 16)
#define OTPC_TIMEOUT 10000
#define OTPC_TIMEOUT_PROG 100000
#define RV1126_OTP_NVM_CEB 0x00
#define RV1126_OTP_NVM_RSTB 0x04
@@ -60,7 +61,14 @@
#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
struct rockchip_otp {
struct device *dev;
@@ -68,6 +76,7 @@ struct rockchip_otp {
struct clk_bulk_data *clks;
int num_clks;
struct reset_control *rst;
struct nvmem_config *config;
};
struct rockchip_data {
@@ -75,6 +84,7 @@ struct rockchip_data {
const char * const *clocks;
int num_clks;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
int (*init)(struct rockchip_otp *otp);
};
@@ -208,6 +218,8 @@ static int rv1126_otp_init(struct rockchip_otp *otp)
return ret;
}
otp->config->read_only = false;
return 0;
}
@@ -236,6 +248,61 @@ static int rv1126_otp_read(void *context, unsigned int offset, void *val,
return 0;
}
static int rv1126_otp_prog(struct rockchip_otp *otp, u32 bit_offset, u32 data,
u32 bit_len)
{
u32 status = 0;
int ret = 0;
if (!data)
return 0;
writel(bit_offset, otp->base + RV1126_OTP_NVM_PRADDR);
writel(bit_len - 1, otp->base + RV1126_OTP_NVM_PRLEN);
writel(data, otp->base + RV1126_OTP_NVM_PRDATA);
writel(1, otp->base + RV1126_OTP_NVM_PRSTART);
/* Wait max 100 ms */
ret = readl_poll_timeout_atomic(otp->base + RV1126_OTP_NVM_PRSTATE,
status, status == 0, 1,
OTPC_TIMEOUT_PROG);
if (ret < 0)
dev_err(otp->dev, "timeout during prog\n");
return ret;
}
static int rv1126_otp_write(void *context, unsigned int offset, void *val,
size_t bytes)
{
struct rockchip_otp *otp = context;
u8 *buf = val;
u8 val_r, val_w;
int ret = 0;
while (bytes--) {
ret = rv1126_otp_read(context, offset, &val_r, 1);
if (ret)
return ret;
val_w = *buf & (~val_r);
ret = rv1126_otp_prog(otp, offset * 8, val_w, 8);
if (ret)
return ret;
buf++;
offset++;
}
return 0;
}
static int rv1126_otp_oem_write(void *context, unsigned int offset, void *val,
size_t bytes)
{
if (offset < 256 || offset > 511 || bytes > 256 || offset + bytes > 512)
return -EINVAL;
return rv1126_otp_write(context, offset, val, bytes);
}
static struct nvmem_config otp_config = {
.name = "rockchip-otp",
.owner = THIS_MODULE,
@@ -265,6 +332,7 @@ static const struct rockchip_data rv1126_data = {
.num_clks = ARRAY_SIZE(rv1126_otp_clocks),
.init = rv1126_otp_init,
.reg_read = rv1126_otp_read,
.reg_write = rv1126_otp_oem_write,
};
static const struct of_device_id rockchip_otp_match[] = {
@@ -325,17 +393,20 @@ static int rockchip_otp_probe(struct platform_device *pdev)
if (IS_ERR(otp->rst))
return PTR_ERR(otp->rst);
otp->config = &otp_config;
otp->config->size = data->size;
otp->config->reg_read = data->reg_read;
otp->config->reg_write = data->reg_write;
otp->config->priv = otp;
otp->config->dev = dev;
if (data->init) {
ret = data->init(otp);
if (ret)
return ret;
}
otp_config.size = data->size;
otp_config.reg_read = data->reg_read;
otp_config.priv = otp;
otp_config.dev = dev;
nvmem = devm_nvmem_register(dev, &otp_config);
nvmem = devm_nvmem_register(dev, otp->config);
return PTR_ERR_OR_ZERO(nvmem);
}