rockchip-otp.c (6439B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Rockchip OTP Driver 4 * 5 * Copyright (c) 2018 Rockchip Electronics Co. Ltd. 6 * Author: Finley Xiao <finley.xiao@rock-chips.com> 7 */ 8 9#include <linux/clk.h> 10#include <linux/delay.h> 11#include <linux/device.h> 12#include <linux/io.h> 13#include <linux/iopoll.h> 14#include <linux/module.h> 15#include <linux/nvmem-provider.h> 16#include <linux/reset.h> 17#include <linux/slab.h> 18#include <linux/of.h> 19#include <linux/of_platform.h> 20#include <linux/platform_device.h> 21 22/* OTP Register Offsets */ 23#define OTPC_SBPI_CTRL 0x0020 24#define OTPC_SBPI_CMD_VALID_PRE 0x0024 25#define OTPC_SBPI_CS_VALID_PRE 0x0028 26#define OTPC_SBPI_STATUS 0x002C 27#define OTPC_USER_CTRL 0x0100 28#define OTPC_USER_ADDR 0x0104 29#define OTPC_USER_ENABLE 0x0108 30#define OTPC_USER_Q 0x0124 31#define OTPC_INT_STATUS 0x0304 32#define OTPC_SBPI_CMD0_OFFSET 0x1000 33#define OTPC_SBPI_CMD1_OFFSET 0x1004 34 35/* OTP Register bits and masks */ 36#define OTPC_USER_ADDR_MASK GENMASK(31, 16) 37#define OTPC_USE_USER BIT(0) 38#define OTPC_USE_USER_MASK GENMASK(16, 16) 39#define OTPC_USER_FSM_ENABLE BIT(0) 40#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) 41#define OTPC_SBPI_DONE BIT(1) 42#define OTPC_USER_DONE BIT(2) 43 44#define SBPI_DAP_ADDR 0x02 45#define SBPI_DAP_ADDR_SHIFT 8 46#define SBPI_DAP_ADDR_MASK GENMASK(31, 24) 47#define SBPI_CMD_VALID_MASK GENMASK(31, 16) 48#define SBPI_DAP_CMD_WRF 0xC0 49#define SBPI_DAP_REG_ECC 0x3A 50#define SBPI_ECC_ENABLE 0x00 51#define SBPI_ECC_DISABLE 0x09 52#define SBPI_ENABLE BIT(0) 53#define SBPI_ENABLE_MASK GENMASK(16, 16) 54 55#define OTPC_TIMEOUT 10000 56 57struct rockchip_otp { 58 struct device *dev; 59 void __iomem *base; 60 struct clk_bulk_data *clks; 61 int num_clks; 62 struct reset_control *rst; 63}; 64 65/* list of required clocks */ 66static const char * const rockchip_otp_clocks[] = { 67 "otp", "apb_pclk", "phy", 68}; 69 70struct rockchip_data { 71 int size; 72}; 73 74static int rockchip_otp_reset(struct rockchip_otp *otp) 75{ 76 int ret; 77 78 ret = reset_control_assert(otp->rst); 79 if (ret) { 80 dev_err(otp->dev, "failed to assert otp phy %d\n", ret); 81 return ret; 82 } 83 84 udelay(2); 85 86 ret = reset_control_deassert(otp->rst); 87 if (ret) { 88 dev_err(otp->dev, "failed to deassert otp phy %d\n", ret); 89 return ret; 90 } 91 92 return 0; 93} 94 95static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag) 96{ 97 u32 status = 0; 98 int ret; 99 100 ret = readl_poll_timeout_atomic(otp->base + OTPC_INT_STATUS, status, 101 (status & flag), 1, OTPC_TIMEOUT); 102 if (ret) 103 return ret; 104 105 /* clean int status */ 106 writel(flag, otp->base + OTPC_INT_STATUS); 107 108 return 0; 109} 110 111static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable) 112{ 113 int ret = 0; 114 115 writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), 116 otp->base + OTPC_SBPI_CTRL); 117 118 writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 119 writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, 120 otp->base + OTPC_SBPI_CMD0_OFFSET); 121 if (enable) 122 writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 123 else 124 writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 125 126 writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 127 128 ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); 129 if (ret < 0) 130 dev_err(otp->dev, "timeout during ecc_enable\n"); 131 132 return ret; 133} 134 135static int rockchip_otp_read(void *context, unsigned int offset, 136 void *val, size_t bytes) 137{ 138 struct rockchip_otp *otp = context; 139 u8 *buf = val; 140 int ret = 0; 141 142 ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks); 143 if (ret < 0) { 144 dev_err(otp->dev, "failed to prepare/enable clks\n"); 145 return ret; 146 } 147 148 ret = rockchip_otp_reset(otp); 149 if (ret) { 150 dev_err(otp->dev, "failed to reset otp phy\n"); 151 goto disable_clks; 152 } 153 154 ret = rockchip_otp_ecc_enable(otp, false); 155 if (ret < 0) { 156 dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); 157 goto disable_clks; 158 } 159 160 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 161 udelay(5); 162 while (bytes--) { 163 writel(offset++ | OTPC_USER_ADDR_MASK, 164 otp->base + OTPC_USER_ADDR); 165 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, 166 otp->base + OTPC_USER_ENABLE); 167 ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); 168 if (ret < 0) { 169 dev_err(otp->dev, "timeout during read setup\n"); 170 goto read_end; 171 } 172 *buf++ = readb(otp->base + OTPC_USER_Q); 173 } 174 175read_end: 176 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 177disable_clks: 178 clk_bulk_disable_unprepare(otp->num_clks, otp->clks); 179 180 return ret; 181} 182 183static struct nvmem_config otp_config = { 184 .name = "rockchip-otp", 185 .owner = THIS_MODULE, 186 .read_only = true, 187 .stride = 1, 188 .word_size = 1, 189 .reg_read = rockchip_otp_read, 190}; 191 192static const struct rockchip_data px30_data = { 193 .size = 0x40, 194}; 195 196static const struct of_device_id rockchip_otp_match[] = { 197 { 198 .compatible = "rockchip,px30-otp", 199 .data = (void *)&px30_data, 200 }, 201 { 202 .compatible = "rockchip,rk3308-otp", 203 .data = (void *)&px30_data, 204 }, 205 { /* sentinel */ }, 206}; 207MODULE_DEVICE_TABLE(of, rockchip_otp_match); 208 209static int rockchip_otp_probe(struct platform_device *pdev) 210{ 211 struct device *dev = &pdev->dev; 212 struct rockchip_otp *otp; 213 const struct rockchip_data *data; 214 struct nvmem_device *nvmem; 215 int ret, i; 216 217 data = of_device_get_match_data(dev); 218 if (!data) { 219 dev_err(dev, "failed to get match data\n"); 220 return -EINVAL; 221 } 222 223 otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), 224 GFP_KERNEL); 225 if (!otp) 226 return -ENOMEM; 227 228 otp->dev = dev; 229 otp->base = devm_platform_ioremap_resource(pdev, 0); 230 if (IS_ERR(otp->base)) 231 return PTR_ERR(otp->base); 232 233 otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks); 234 otp->clks = devm_kcalloc(dev, otp->num_clks, 235 sizeof(*otp->clks), GFP_KERNEL); 236 if (!otp->clks) 237 return -ENOMEM; 238 239 for (i = 0; i < otp->num_clks; ++i) 240 otp->clks[i].id = rockchip_otp_clocks[i]; 241 242 ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks); 243 if (ret) 244 return ret; 245 246 otp->rst = devm_reset_control_get(dev, "phy"); 247 if (IS_ERR(otp->rst)) 248 return PTR_ERR(otp->rst); 249 250 otp_config.size = data->size; 251 otp_config.priv = otp; 252 otp_config.dev = dev; 253 nvmem = devm_nvmem_register(dev, &otp_config); 254 255 return PTR_ERR_OR_ZERO(nvmem); 256} 257 258static struct platform_driver rockchip_otp_driver = { 259 .probe = rockchip_otp_probe, 260 .driver = { 261 .name = "rockchip-otp", 262 .of_match_table = rockchip_otp_match, 263 }, 264}; 265 266module_platform_driver(rockchip_otp_driver); 267MODULE_DESCRIPTION("Rockchip OTP driver"); 268MODULE_LICENSE("GPL v2");