diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index 2d1e6015ce42..1919d7af2ae8 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -27,6 +27,7 @@ #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 @@ -54,6 +55,7 @@ #define OTPC_TIMEOUT 10000 #define OTPC_TIMEOUT_PROG 100000 +#define RK3568_NBYTES 2 #define RV1126_OTP_NVM_CEB 0x00 #define RV1126_OTP_NVM_RSTB 0x04 @@ -249,6 +251,79 @@ disable_clks: return ret; } +static int rk3568_otp_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct rockchip_otp *otp = context; + unsigned int addr_start, addr_end, addr_offset, addr_len; + unsigned int otp_qp; + u32 out_value; + u8 *buf; + int ret = 0, i = 0; + + addr_start = rounddown(offset, RK3568_NBYTES) / RK3568_NBYTES; + addr_end = roundup(offset + bytes, RK3568_NBYTES) / RK3568_NBYTES; + addr_offset = offset % RK3568_NBYTES; + addr_len = addr_end - addr_start; + + buf = kzalloc(array3_size(addr_len, RK3568_NBYTES, sizeof(*buf)), + GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks); + if (ret < 0) { + dev_err(otp->dev, "failed to prepare/enable clks\n"); + goto out; + } + + ret = rockchip_otp_reset(otp); + if (ret) { + dev_err(otp->dev, "failed to reset otp phy\n"); + goto disable_clks; + } + + ret = px30_otp_ecc_enable(otp, true); + if (ret < 0) { + dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); + goto disable_clks; + } + + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + while (addr_len--) { + writel(addr_start++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + ret = px30_otp_wait_status(otp, OTPC_USER_DONE); + if (ret < 0) { + dev_err(otp->dev, "timeout during read setup\n"); + goto read_end; + } + otp_qp = readl(otp->base + OTPC_USER_QP); + if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20)) { + ret = -EIO; + dev_err(otp->dev, "ecc check error during read setup\n"); + goto read_end; + } + out_value = readl(otp->base + OTPC_USER_Q); + memcpy(&buf[i], &out_value, RK3568_NBYTES); + i += RK3568_NBYTES; + } + + memcpy(val, buf + addr_offset, bytes); + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); +disable_clks: + clk_bulk_disable_unprepare(otp->num_clks, otp->clks); +out: + kfree(buf); + + return ret; +} + static int rv1126_otp_init(struct rockchip_otp *otp) { u32 status = 0; @@ -429,6 +504,17 @@ static const struct rockchip_data px30_data = { .reg_read = px30_otp_read, }; +static const char * const rk3568_otp_clocks[] = { + "usr", "sbpi", "apb", "phy", +}; + +static const struct rockchip_data rk3568_data = { + .size = 0x80, + .clocks = rk3568_otp_clocks, + .num_clks = ARRAY_SIZE(rk3568_otp_clocks), + .reg_read = rk3568_otp_read, +}; + static const char * const rv1126_otp_clocks[] = { "otp", "apb_pclk", }; @@ -451,6 +537,10 @@ static const struct of_device_id rockchip_otp_match[] = { .compatible = "rockchip,rk3308-otp", .data = (void *)&px30_data, }, + { + .compatible = "rockchip,rk3568-otp", + .data = (void *)&rk3568_data, + }, { .compatible = "rockchip,rv1126-otp", .data = (void *)&rv1126_data,