162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Rockchip OTP Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2018 Rockchip Electronics Co. Ltd. 662306a36Sopenharmony_ci * Author: Finley Xiao <finley.xiao@rock-chips.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/nvmem-provider.h> 1662306a36Sopenharmony_ci#include <linux/reset.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_platform.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* OTP Register Offsets */ 2362306a36Sopenharmony_ci#define OTPC_SBPI_CTRL 0x0020 2462306a36Sopenharmony_ci#define OTPC_SBPI_CMD_VALID_PRE 0x0024 2562306a36Sopenharmony_ci#define OTPC_SBPI_CS_VALID_PRE 0x0028 2662306a36Sopenharmony_ci#define OTPC_SBPI_STATUS 0x002C 2762306a36Sopenharmony_ci#define OTPC_USER_CTRL 0x0100 2862306a36Sopenharmony_ci#define OTPC_USER_ADDR 0x0104 2962306a36Sopenharmony_ci#define OTPC_USER_ENABLE 0x0108 3062306a36Sopenharmony_ci#define OTPC_USER_Q 0x0124 3162306a36Sopenharmony_ci#define OTPC_INT_STATUS 0x0304 3262306a36Sopenharmony_ci#define OTPC_SBPI_CMD0_OFFSET 0x1000 3362306a36Sopenharmony_ci#define OTPC_SBPI_CMD1_OFFSET 0x1004 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* OTP Register bits and masks */ 3662306a36Sopenharmony_ci#define OTPC_USER_ADDR_MASK GENMASK(31, 16) 3762306a36Sopenharmony_ci#define OTPC_USE_USER BIT(0) 3862306a36Sopenharmony_ci#define OTPC_USE_USER_MASK GENMASK(16, 16) 3962306a36Sopenharmony_ci#define OTPC_USER_FSM_ENABLE BIT(0) 4062306a36Sopenharmony_ci#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) 4162306a36Sopenharmony_ci#define OTPC_SBPI_DONE BIT(1) 4262306a36Sopenharmony_ci#define OTPC_USER_DONE BIT(2) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define SBPI_DAP_ADDR 0x02 4562306a36Sopenharmony_ci#define SBPI_DAP_ADDR_SHIFT 8 4662306a36Sopenharmony_ci#define SBPI_DAP_ADDR_MASK GENMASK(31, 24) 4762306a36Sopenharmony_ci#define SBPI_CMD_VALID_MASK GENMASK(31, 16) 4862306a36Sopenharmony_ci#define SBPI_DAP_CMD_WRF 0xC0 4962306a36Sopenharmony_ci#define SBPI_DAP_REG_ECC 0x3A 5062306a36Sopenharmony_ci#define SBPI_ECC_ENABLE 0x00 5162306a36Sopenharmony_ci#define SBPI_ECC_DISABLE 0x09 5262306a36Sopenharmony_ci#define SBPI_ENABLE BIT(0) 5362306a36Sopenharmony_ci#define SBPI_ENABLE_MASK GENMASK(16, 16) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define OTPC_TIMEOUT 10000 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* RK3588 Register */ 5862306a36Sopenharmony_ci#define RK3588_OTPC_AUTO_CTRL 0x04 5962306a36Sopenharmony_ci#define RK3588_OTPC_AUTO_EN 0x08 6062306a36Sopenharmony_ci#define RK3588_OTPC_INT_ST 0x84 6162306a36Sopenharmony_ci#define RK3588_OTPC_DOUT0 0x20 6262306a36Sopenharmony_ci#define RK3588_NO_SECURE_OFFSET 0x300 6362306a36Sopenharmony_ci#define RK3588_NBYTES 4 6462306a36Sopenharmony_ci#define RK3588_BURST_NUM 1 6562306a36Sopenharmony_ci#define RK3588_BURST_SHIFT 8 6662306a36Sopenharmony_ci#define RK3588_ADDR_SHIFT 16 6762306a36Sopenharmony_ci#define RK3588_AUTO_EN BIT(0) 6862306a36Sopenharmony_ci#define RK3588_RD_DONE BIT(1) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct rockchip_data { 7162306a36Sopenharmony_ci int size; 7262306a36Sopenharmony_ci const char * const *clks; 7362306a36Sopenharmony_ci int num_clks; 7462306a36Sopenharmony_ci nvmem_reg_read_t reg_read; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct rockchip_otp { 7862306a36Sopenharmony_ci struct device *dev; 7962306a36Sopenharmony_ci void __iomem *base; 8062306a36Sopenharmony_ci struct clk_bulk_data *clks; 8162306a36Sopenharmony_ci struct reset_control *rst; 8262306a36Sopenharmony_ci const struct rockchip_data *data; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int rockchip_otp_reset(struct rockchip_otp *otp) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = reset_control_assert(otp->rst); 9062306a36Sopenharmony_ci if (ret) { 9162306a36Sopenharmony_ci dev_err(otp->dev, "failed to assert otp phy %d\n", ret); 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci udelay(2); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ret = reset_control_deassert(otp->rst); 9862306a36Sopenharmony_ci if (ret) { 9962306a36Sopenharmony_ci dev_err(otp->dev, "failed to deassert otp phy %d\n", ret); 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int rockchip_otp_wait_status(struct rockchip_otp *otp, 10762306a36Sopenharmony_ci unsigned int reg, u32 flag) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 status = 0; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(otp->base + reg, status, 11362306a36Sopenharmony_ci (status & flag), 1, OTPC_TIMEOUT); 11462306a36Sopenharmony_ci if (ret) 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* clean int status */ 11862306a36Sopenharmony_ci writel(flag, otp->base + reg); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int ret = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), 12862306a36Sopenharmony_ci otp->base + OTPC_SBPI_CTRL); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 13162306a36Sopenharmony_ci writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, 13262306a36Sopenharmony_ci otp->base + OTPC_SBPI_CMD0_OFFSET); 13362306a36Sopenharmony_ci if (enable) 13462306a36Sopenharmony_ci writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 13562306a36Sopenharmony_ci else 13662306a36Sopenharmony_ci writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_SBPI_DONE); 14162306a36Sopenharmony_ci if (ret < 0) 14262306a36Sopenharmony_ci dev_err(otp->dev, "timeout during ecc_enable\n"); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int px30_otp_read(void *context, unsigned int offset, 14862306a36Sopenharmony_ci void *val, size_t bytes) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct rockchip_otp *otp = context; 15162306a36Sopenharmony_ci u8 *buf = val; 15262306a36Sopenharmony_ci int ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = rockchip_otp_reset(otp); 15562306a36Sopenharmony_ci if (ret) { 15662306a36Sopenharmony_ci dev_err(otp->dev, "failed to reset otp phy\n"); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = rockchip_otp_ecc_enable(otp, false); 16162306a36Sopenharmony_ci if (ret < 0) { 16262306a36Sopenharmony_ci dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 16762306a36Sopenharmony_ci udelay(5); 16862306a36Sopenharmony_ci while (bytes--) { 16962306a36Sopenharmony_ci writel(offset++ | OTPC_USER_ADDR_MASK, 17062306a36Sopenharmony_ci otp->base + OTPC_USER_ADDR); 17162306a36Sopenharmony_ci writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, 17262306a36Sopenharmony_ci otp->base + OTPC_USER_ENABLE); 17362306a36Sopenharmony_ci ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_USER_DONE); 17462306a36Sopenharmony_ci if (ret < 0) { 17562306a36Sopenharmony_ci dev_err(otp->dev, "timeout during read setup\n"); 17662306a36Sopenharmony_ci goto read_end; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci *buf++ = readb(otp->base + OTPC_USER_Q); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciread_end: 18262306a36Sopenharmony_ci writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int rk3588_otp_read(void *context, unsigned int offset, 18862306a36Sopenharmony_ci void *val, size_t bytes) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct rockchip_otp *otp = context; 19162306a36Sopenharmony_ci unsigned int addr_start, addr_end, addr_len; 19262306a36Sopenharmony_ci int ret, i = 0; 19362306a36Sopenharmony_ci u32 data; 19462306a36Sopenharmony_ci u8 *buf; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci addr_start = round_down(offset, RK3588_NBYTES) / RK3588_NBYTES; 19762306a36Sopenharmony_ci addr_end = round_up(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES; 19862306a36Sopenharmony_ci addr_len = addr_end - addr_start; 19962306a36Sopenharmony_ci addr_start += RK3588_NO_SECURE_OFFSET; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci buf = kzalloc(array_size(addr_len, RK3588_NBYTES), GFP_KERNEL); 20262306a36Sopenharmony_ci if (!buf) 20362306a36Sopenharmony_ci return -ENOMEM; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci while (addr_len--) { 20662306a36Sopenharmony_ci writel((addr_start << RK3588_ADDR_SHIFT) | 20762306a36Sopenharmony_ci (RK3588_BURST_NUM << RK3588_BURST_SHIFT), 20862306a36Sopenharmony_ci otp->base + RK3588_OTPC_AUTO_CTRL); 20962306a36Sopenharmony_ci writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = rockchip_otp_wait_status(otp, RK3588_OTPC_INT_ST, 21262306a36Sopenharmony_ci RK3588_RD_DONE); 21362306a36Sopenharmony_ci if (ret < 0) { 21462306a36Sopenharmony_ci dev_err(otp->dev, "timeout during read setup\n"); 21562306a36Sopenharmony_ci goto read_end; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci data = readl(otp->base + RK3588_OTPC_DOUT0); 21962306a36Sopenharmony_ci memcpy(&buf[i], &data, RK3588_NBYTES); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci i += RK3588_NBYTES; 22262306a36Sopenharmony_ci addr_start++; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci memcpy(val, buf + offset % RK3588_NBYTES, bytes); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciread_end: 22862306a36Sopenharmony_ci kfree(buf); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int rockchip_otp_read(void *context, unsigned int offset, 23462306a36Sopenharmony_ci void *val, size_t bytes) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct rockchip_otp *otp = context; 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!otp->data || !otp->data->reg_read) 24062306a36Sopenharmony_ci return -EINVAL; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(otp->data->num_clks, otp->clks); 24362306a36Sopenharmony_ci if (ret < 0) { 24462306a36Sopenharmony_ci dev_err(otp->dev, "failed to prepare/enable clks\n"); 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = otp->data->reg_read(context, offset, val, bytes); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic struct nvmem_config otp_config = { 25662306a36Sopenharmony_ci .name = "rockchip-otp", 25762306a36Sopenharmony_ci .owner = THIS_MODULE, 25862306a36Sopenharmony_ci .read_only = true, 25962306a36Sopenharmony_ci .stride = 1, 26062306a36Sopenharmony_ci .word_size = 1, 26162306a36Sopenharmony_ci .reg_read = rockchip_otp_read, 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic const char * const px30_otp_clocks[] = { 26562306a36Sopenharmony_ci "otp", "apb_pclk", "phy", 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct rockchip_data px30_data = { 26962306a36Sopenharmony_ci .size = 0x40, 27062306a36Sopenharmony_ci .clks = px30_otp_clocks, 27162306a36Sopenharmony_ci .num_clks = ARRAY_SIZE(px30_otp_clocks), 27262306a36Sopenharmony_ci .reg_read = px30_otp_read, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const char * const rk3588_otp_clocks[] = { 27662306a36Sopenharmony_ci "otp", "apb_pclk", "phy", "arb", 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic const struct rockchip_data rk3588_data = { 28062306a36Sopenharmony_ci .size = 0x400, 28162306a36Sopenharmony_ci .clks = rk3588_otp_clocks, 28262306a36Sopenharmony_ci .num_clks = ARRAY_SIZE(rk3588_otp_clocks), 28362306a36Sopenharmony_ci .reg_read = rk3588_otp_read, 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct of_device_id rockchip_otp_match[] = { 28762306a36Sopenharmony_ci { 28862306a36Sopenharmony_ci .compatible = "rockchip,px30-otp", 28962306a36Sopenharmony_ci .data = &px30_data, 29062306a36Sopenharmony_ci }, 29162306a36Sopenharmony_ci { 29262306a36Sopenharmony_ci .compatible = "rockchip,rk3308-otp", 29362306a36Sopenharmony_ci .data = &px30_data, 29462306a36Sopenharmony_ci }, 29562306a36Sopenharmony_ci { 29662306a36Sopenharmony_ci .compatible = "rockchip,rk3588-otp", 29762306a36Sopenharmony_ci .data = &rk3588_data, 29862306a36Sopenharmony_ci }, 29962306a36Sopenharmony_ci { /* sentinel */ }, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_otp_match); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int rockchip_otp_probe(struct platform_device *pdev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 30662306a36Sopenharmony_ci struct rockchip_otp *otp; 30762306a36Sopenharmony_ci const struct rockchip_data *data; 30862306a36Sopenharmony_ci struct nvmem_device *nvmem; 30962306a36Sopenharmony_ci int ret, i; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci data = of_device_get_match_data(dev); 31262306a36Sopenharmony_ci if (!data) 31362306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, "failed to get match data\n"); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), 31662306a36Sopenharmony_ci GFP_KERNEL); 31762306a36Sopenharmony_ci if (!otp) 31862306a36Sopenharmony_ci return -ENOMEM; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci otp->data = data; 32162306a36Sopenharmony_ci otp->dev = dev; 32262306a36Sopenharmony_ci otp->base = devm_platform_ioremap_resource(pdev, 0); 32362306a36Sopenharmony_ci if (IS_ERR(otp->base)) 32462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(otp->base), 32562306a36Sopenharmony_ci "failed to ioremap resource\n"); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks), 32862306a36Sopenharmony_ci GFP_KERNEL); 32962306a36Sopenharmony_ci if (!otp->clks) 33062306a36Sopenharmony_ci return -ENOMEM; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for (i = 0; i < data->num_clks; ++i) 33362306a36Sopenharmony_ci otp->clks[i].id = data->clks[i]; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, data->num_clks, otp->clks); 33662306a36Sopenharmony_ci if (ret) 33762306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to get clocks\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci otp->rst = devm_reset_control_array_get_exclusive(dev); 34062306a36Sopenharmony_ci if (IS_ERR(otp->rst)) 34162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(otp->rst), 34262306a36Sopenharmony_ci "failed to get resets\n"); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci otp_config.size = data->size; 34562306a36Sopenharmony_ci otp_config.priv = otp; 34662306a36Sopenharmony_ci otp_config.dev = dev; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci nvmem = devm_nvmem_register(dev, &otp_config); 34962306a36Sopenharmony_ci if (IS_ERR(nvmem)) 35062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(nvmem), 35162306a36Sopenharmony_ci "failed to register nvmem device\n"); 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct platform_driver rockchip_otp_driver = { 35662306a36Sopenharmony_ci .probe = rockchip_otp_probe, 35762306a36Sopenharmony_ci .driver = { 35862306a36Sopenharmony_ci .name = "rockchip-otp", 35962306a36Sopenharmony_ci .of_match_table = rockchip_otp_match, 36062306a36Sopenharmony_ci }, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cimodule_platform_driver(rockchip_otp_driver); 36462306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip OTP driver"); 36562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 366