1755864feSFinley Xiao // SPDX-License-Identifier: GPL-2.0-only 2755864feSFinley Xiao /* 3755864feSFinley Xiao * Rockchip OTP Driver 4755864feSFinley Xiao * 5755864feSFinley Xiao * Copyright (c) 2018 Rockchip Electronics Co. Ltd. 6755864feSFinley Xiao * Author: Finley Xiao <finley.xiao@rock-chips.com> 7755864feSFinley Xiao */ 8755864feSFinley Xiao 9755864feSFinley Xiao #include <linux/clk.h> 10755864feSFinley Xiao #include <linux/delay.h> 11755864feSFinley Xiao #include <linux/device.h> 12755864feSFinley Xiao #include <linux/io.h> 13755864feSFinley Xiao #include <linux/iopoll.h> 14755864feSFinley Xiao #include <linux/module.h> 15755864feSFinley Xiao #include <linux/nvmem-provider.h> 16755864feSFinley Xiao #include <linux/reset.h> 17755864feSFinley Xiao #include <linux/slab.h> 18755864feSFinley Xiao #include <linux/of.h> 19755864feSFinley Xiao #include <linux/of_platform.h> 20755864feSFinley Xiao #include <linux/platform_device.h> 21755864feSFinley Xiao 22755864feSFinley Xiao /* OTP Register Offsets */ 23755864feSFinley Xiao #define OTPC_SBPI_CTRL 0x0020 24755864feSFinley Xiao #define OTPC_SBPI_CMD_VALID_PRE 0x0024 25755864feSFinley Xiao #define OTPC_SBPI_CS_VALID_PRE 0x0028 26755864feSFinley Xiao #define OTPC_SBPI_STATUS 0x002C 27755864feSFinley Xiao #define OTPC_USER_CTRL 0x0100 28755864feSFinley Xiao #define OTPC_USER_ADDR 0x0104 29755864feSFinley Xiao #define OTPC_USER_ENABLE 0x0108 30755864feSFinley Xiao #define OTPC_USER_Q 0x0124 31755864feSFinley Xiao #define OTPC_INT_STATUS 0x0304 32755864feSFinley Xiao #define OTPC_SBPI_CMD0_OFFSET 0x1000 33755864feSFinley Xiao #define OTPC_SBPI_CMD1_OFFSET 0x1004 34755864feSFinley Xiao 35755864feSFinley Xiao /* OTP Register bits and masks */ 36755864feSFinley Xiao #define OTPC_USER_ADDR_MASK GENMASK(31, 16) 37755864feSFinley Xiao #define OTPC_USE_USER BIT(0) 38755864feSFinley Xiao #define OTPC_USE_USER_MASK GENMASK(16, 16) 39755864feSFinley Xiao #define OTPC_USER_FSM_ENABLE BIT(0) 40755864feSFinley Xiao #define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) 41755864feSFinley Xiao #define OTPC_SBPI_DONE BIT(1) 42755864feSFinley Xiao #define OTPC_USER_DONE BIT(2) 43755864feSFinley Xiao 44755864feSFinley Xiao #define SBPI_DAP_ADDR 0x02 45755864feSFinley Xiao #define SBPI_DAP_ADDR_SHIFT 8 46755864feSFinley Xiao #define SBPI_DAP_ADDR_MASK GENMASK(31, 24) 47755864feSFinley Xiao #define SBPI_CMD_VALID_MASK GENMASK(31, 16) 48755864feSFinley Xiao #define SBPI_DAP_CMD_WRF 0xC0 49755864feSFinley Xiao #define SBPI_DAP_REG_ECC 0x3A 50755864feSFinley Xiao #define SBPI_ECC_ENABLE 0x00 51755864feSFinley Xiao #define SBPI_ECC_DISABLE 0x09 52755864feSFinley Xiao #define SBPI_ENABLE BIT(0) 53755864feSFinley Xiao #define SBPI_ENABLE_MASK GENMASK(16, 16) 54755864feSFinley Xiao 55755864feSFinley Xiao #define OTPC_TIMEOUT 10000 56755864feSFinley Xiao 57755864feSFinley Xiao struct rockchip_otp { 58755864feSFinley Xiao struct device *dev; 59755864feSFinley Xiao void __iomem *base; 60755864feSFinley Xiao struct clk_bulk_data *clks; 61755864feSFinley Xiao int num_clks; 62755864feSFinley Xiao struct reset_control *rst; 63755864feSFinley Xiao }; 64755864feSFinley Xiao 65755864feSFinley Xiao /* list of required clocks */ 66755864feSFinley Xiao static const char * const rockchip_otp_clocks[] = { 67755864feSFinley Xiao "otp", "apb_pclk", "phy", 68755864feSFinley Xiao }; 69755864feSFinley Xiao 70755864feSFinley Xiao struct rockchip_data { 71755864feSFinley Xiao int size; 72755864feSFinley Xiao }; 73755864feSFinley Xiao 74755864feSFinley Xiao static int rockchip_otp_reset(struct rockchip_otp *otp) 75755864feSFinley Xiao { 76755864feSFinley Xiao int ret; 77755864feSFinley Xiao 78755864feSFinley Xiao ret = reset_control_assert(otp->rst); 79755864feSFinley Xiao if (ret) { 80755864feSFinley Xiao dev_err(otp->dev, "failed to assert otp phy %d\n", ret); 81755864feSFinley Xiao return ret; 82755864feSFinley Xiao } 83755864feSFinley Xiao 84755864feSFinley Xiao udelay(2); 85755864feSFinley Xiao 86755864feSFinley Xiao ret = reset_control_deassert(otp->rst); 87755864feSFinley Xiao if (ret) { 88755864feSFinley Xiao dev_err(otp->dev, "failed to deassert otp phy %d\n", ret); 89755864feSFinley Xiao return ret; 90755864feSFinley Xiao } 91755864feSFinley Xiao 92755864feSFinley Xiao return 0; 93755864feSFinley Xiao } 94755864feSFinley Xiao 95755864feSFinley Xiao static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag) 96755864feSFinley Xiao { 97755864feSFinley Xiao u32 status = 0; 98755864feSFinley Xiao int ret; 99755864feSFinley Xiao 100755864feSFinley Xiao ret = readl_poll_timeout_atomic(otp->base + OTPC_INT_STATUS, status, 101755864feSFinley Xiao (status & flag), 1, OTPC_TIMEOUT); 102755864feSFinley Xiao if (ret) 103755864feSFinley Xiao return ret; 104755864feSFinley Xiao 105755864feSFinley Xiao /* clean int status */ 106755864feSFinley Xiao writel(flag, otp->base + OTPC_INT_STATUS); 107755864feSFinley Xiao 108755864feSFinley Xiao return 0; 109755864feSFinley Xiao } 110755864feSFinley Xiao 111755864feSFinley Xiao static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable) 112755864feSFinley Xiao { 113755864feSFinley Xiao int ret = 0; 114755864feSFinley Xiao 115755864feSFinley Xiao writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), 116755864feSFinley Xiao otp->base + OTPC_SBPI_CTRL); 117755864feSFinley Xiao 118755864feSFinley Xiao writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 119755864feSFinley Xiao writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, 120755864feSFinley Xiao otp->base + OTPC_SBPI_CMD0_OFFSET); 121755864feSFinley Xiao if (enable) 122755864feSFinley Xiao writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 123755864feSFinley Xiao else 124755864feSFinley Xiao writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 125755864feSFinley Xiao 126755864feSFinley Xiao writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 127755864feSFinley Xiao 128755864feSFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); 129755864feSFinley Xiao if (ret < 0) 130755864feSFinley Xiao dev_err(otp->dev, "timeout during ecc_enable\n"); 131755864feSFinley Xiao 132755864feSFinley Xiao return ret; 133755864feSFinley Xiao } 134755864feSFinley Xiao 135755864feSFinley Xiao static int rockchip_otp_read(void *context, unsigned int offset, 136755864feSFinley Xiao void *val, size_t bytes) 137755864feSFinley Xiao { 138755864feSFinley Xiao struct rockchip_otp *otp = context; 139755864feSFinley Xiao u8 *buf = val; 140755864feSFinley Xiao int ret = 0; 141755864feSFinley Xiao 142755864feSFinley Xiao ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks); 143755864feSFinley Xiao if (ret < 0) { 144755864feSFinley Xiao dev_err(otp->dev, "failed to prepare/enable clks\n"); 145755864feSFinley Xiao return ret; 146755864feSFinley Xiao } 147755864feSFinley Xiao 148755864feSFinley Xiao ret = rockchip_otp_reset(otp); 149755864feSFinley Xiao if (ret) { 150755864feSFinley Xiao dev_err(otp->dev, "failed to reset otp phy\n"); 151755864feSFinley Xiao goto disable_clks; 152755864feSFinley Xiao } 153755864feSFinley Xiao 154755864feSFinley Xiao ret = rockchip_otp_ecc_enable(otp, false); 155755864feSFinley Xiao if (ret < 0) { 156755864feSFinley Xiao dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); 157755864feSFinley Xiao goto disable_clks; 158755864feSFinley Xiao } 159755864feSFinley Xiao 160755864feSFinley Xiao writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 161755864feSFinley Xiao udelay(5); 162755864feSFinley Xiao while (bytes--) { 163755864feSFinley Xiao writel(offset++ | OTPC_USER_ADDR_MASK, 164755864feSFinley Xiao otp->base + OTPC_USER_ADDR); 165755864feSFinley Xiao writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, 166755864feSFinley Xiao otp->base + OTPC_USER_ENABLE); 167755864feSFinley Xiao ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); 168755864feSFinley Xiao if (ret < 0) { 169755864feSFinley Xiao dev_err(otp->dev, "timeout during read setup\n"); 170755864feSFinley Xiao goto read_end; 171755864feSFinley Xiao } 172755864feSFinley Xiao *buf++ = readb(otp->base + OTPC_USER_Q); 173755864feSFinley Xiao } 174755864feSFinley Xiao 175755864feSFinley Xiao read_end: 176755864feSFinley Xiao writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 177755864feSFinley Xiao disable_clks: 178755864feSFinley Xiao clk_bulk_disable_unprepare(otp->num_clks, otp->clks); 179755864feSFinley Xiao 180755864feSFinley Xiao return ret; 181755864feSFinley Xiao } 182755864feSFinley Xiao 183755864feSFinley Xiao static struct nvmem_config otp_config = { 184755864feSFinley Xiao .name = "rockchip-otp", 185755864feSFinley Xiao .owner = THIS_MODULE, 186755864feSFinley Xiao .read_only = true, 187755864feSFinley Xiao .stride = 1, 188755864feSFinley Xiao .word_size = 1, 189755864feSFinley Xiao .reg_read = rockchip_otp_read, 190755864feSFinley Xiao }; 191755864feSFinley Xiao 192755864feSFinley Xiao static const struct rockchip_data px30_data = { 193755864feSFinley Xiao .size = 0x40, 194755864feSFinley Xiao }; 195755864feSFinley Xiao 196755864feSFinley Xiao static const struct of_device_id rockchip_otp_match[] = { 197755864feSFinley Xiao { 198755864feSFinley Xiao .compatible = "rockchip,px30-otp", 199755864feSFinley Xiao .data = (void *)&px30_data, 200755864feSFinley Xiao }, 201755864feSFinley Xiao { 202755864feSFinley Xiao .compatible = "rockchip,rk3308-otp", 203755864feSFinley Xiao .data = (void *)&px30_data, 204755864feSFinley Xiao }, 205755864feSFinley Xiao { /* sentinel */ }, 206755864feSFinley Xiao }; 207755864feSFinley Xiao MODULE_DEVICE_TABLE(of, rockchip_otp_match); 208755864feSFinley Xiao 209755864feSFinley Xiao static int rockchip_otp_probe(struct platform_device *pdev) 210755864feSFinley Xiao { 211755864feSFinley Xiao struct device *dev = &pdev->dev; 212755864feSFinley Xiao struct rockchip_otp *otp; 213755864feSFinley Xiao const struct rockchip_data *data; 214755864feSFinley Xiao struct nvmem_device *nvmem; 215755864feSFinley Xiao int ret, i; 216755864feSFinley Xiao 217755864feSFinley Xiao data = of_device_get_match_data(dev); 218755864feSFinley Xiao if (!data) { 219755864feSFinley Xiao dev_err(dev, "failed to get match data\n"); 220755864feSFinley Xiao return -EINVAL; 221755864feSFinley Xiao } 222755864feSFinley Xiao 223755864feSFinley Xiao otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), 224755864feSFinley Xiao GFP_KERNEL); 225755864feSFinley Xiao if (!otp) 226755864feSFinley Xiao return -ENOMEM; 227755864feSFinley Xiao 228755864feSFinley Xiao otp->dev = dev; 229755864feSFinley Xiao otp->base = devm_platform_ioremap_resource(pdev, 0); 230755864feSFinley Xiao if (IS_ERR(otp->base)) 231755864feSFinley Xiao return PTR_ERR(otp->base); 232755864feSFinley Xiao 233755864feSFinley Xiao otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks); 234755864feSFinley Xiao otp->clks = devm_kcalloc(dev, otp->num_clks, 235755864feSFinley Xiao sizeof(*otp->clks), GFP_KERNEL); 236755864feSFinley Xiao if (!otp->clks) 237755864feSFinley Xiao return -ENOMEM; 238755864feSFinley Xiao 239755864feSFinley Xiao for (i = 0; i < otp->num_clks; ++i) 240755864feSFinley Xiao otp->clks[i].id = rockchip_otp_clocks[i]; 241755864feSFinley Xiao 242755864feSFinley Xiao ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks); 243755864feSFinley Xiao if (ret) 244755864feSFinley Xiao return ret; 245755864feSFinley Xiao 246755864feSFinley Xiao otp->rst = devm_reset_control_get(dev, "phy"); 247755864feSFinley Xiao if (IS_ERR(otp->rst)) 248755864feSFinley Xiao return PTR_ERR(otp->rst); 249755864feSFinley Xiao 250755864feSFinley Xiao otp_config.size = data->size; 251755864feSFinley Xiao otp_config.priv = otp; 252755864feSFinley Xiao otp_config.dev = dev; 253755864feSFinley Xiao nvmem = devm_nvmem_register(dev, &otp_config); 254755864feSFinley Xiao 255755864feSFinley Xiao return PTR_ERR_OR_ZERO(nvmem); 256755864feSFinley Xiao } 257755864feSFinley Xiao 258755864feSFinley Xiao static struct platform_driver rockchip_otp_driver = { 259755864feSFinley Xiao .probe = rockchip_otp_probe, 260755864feSFinley Xiao .driver = { 261755864feSFinley Xiao .name = "rockchip-otp", 262755864feSFinley Xiao .of_match_table = rockchip_otp_match, 263755864feSFinley Xiao }, 264755864feSFinley Xiao }; 265755864feSFinley Xiao 266755864feSFinley Xiao module_platform_driver(rockchip_otp_driver); 267755864feSFinley Xiao MODULE_DESCRIPTION("Rockchip OTP driver"); 268755864feSFinley Xiao MODULE_LICENSE("GPL v2"); 269