13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Rockchip OTP Driver 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (c) 2018 Rockchip Electronics Co. Ltd. 63d0407baSopenharmony_ci * Author: Finley Xiao <finley.xiao@rock-chips.com> 73d0407baSopenharmony_ci */ 83d0407baSopenharmony_ci 93d0407baSopenharmony_ci#include <linux/clk.h> 103d0407baSopenharmony_ci#include <linux/delay.h> 113d0407baSopenharmony_ci#include <linux/device.h> 123d0407baSopenharmony_ci#include <linux/io.h> 133d0407baSopenharmony_ci#include <linux/iopoll.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/nvmem-provider.h> 163d0407baSopenharmony_ci#include <linux/reset.h> 173d0407baSopenharmony_ci#include <linux/slab.h> 183d0407baSopenharmony_ci#include <linux/of.h> 193d0407baSopenharmony_ci#include <linux/of_platform.h> 203d0407baSopenharmony_ci#include <linux/platform_device.h> 213d0407baSopenharmony_ci 223d0407baSopenharmony_ci/* OTP Register Offsets */ 233d0407baSopenharmony_ci#define OTPC_SBPI_CTRL 0x0020 243d0407baSopenharmony_ci#define OTPC_SBPI_CMD_VALID_PRE 0x0024 253d0407baSopenharmony_ci#define OTPC_SBPI_CS_VALID_PRE 0x0028 263d0407baSopenharmony_ci#define OTPC_SBPI_STATUS 0x002C 273d0407baSopenharmony_ci#define OTPC_USER_CTRL 0x0100 283d0407baSopenharmony_ci#define OTPC_USER_ADDR 0x0104 293d0407baSopenharmony_ci#define OTPC_USER_ENABLE 0x0108 303d0407baSopenharmony_ci#define OTPC_USER_Q 0x0124 313d0407baSopenharmony_ci#define OTPC_INT_STATUS 0x0304 323d0407baSopenharmony_ci#define OTPC_SBPI_CMD0_OFFSET 0x1000 333d0407baSopenharmony_ci#define OTPC_SBPI_CMD1_OFFSET 0x1004 343d0407baSopenharmony_ci 353d0407baSopenharmony_ci/* OTP Register bits and masks */ 363d0407baSopenharmony_ci#define OTPC_USER_ADDR_MASK GENMASK(31, 16) 373d0407baSopenharmony_ci#define OTPC_USE_USER BIT(0) 383d0407baSopenharmony_ci#define OTPC_USE_USER_MASK GENMASK(16, 16) 393d0407baSopenharmony_ci#define OTPC_USER_FSM_ENABLE BIT(0) 403d0407baSopenharmony_ci#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) 413d0407baSopenharmony_ci#define OTPC_SBPI_DONE BIT(1) 423d0407baSopenharmony_ci#define OTPC_USER_DONE BIT(2) 433d0407baSopenharmony_ci 443d0407baSopenharmony_ci#define SBPI_DAP_ADDR 0x02 453d0407baSopenharmony_ci#define SBPI_DAP_ADDR_SHIFT 8 463d0407baSopenharmony_ci#define SBPI_DAP_ADDR_MASK GENMASK(31, 24) 473d0407baSopenharmony_ci#define SBPI_CMD_VALID_MASK GENMASK(31, 16) 483d0407baSopenharmony_ci#define SBPI_DAP_CMD_WRF 0xC0 493d0407baSopenharmony_ci#define SBPI_DAP_REG_ECC 0x3A 503d0407baSopenharmony_ci#define SBPI_ECC_ENABLE 0x00 513d0407baSopenharmony_ci#define SBPI_ECC_DISABLE 0x09 523d0407baSopenharmony_ci#define SBPI_ENABLE BIT(0) 533d0407baSopenharmony_ci#define SBPI_ENABLE_MASK GENMASK(16, 16) 543d0407baSopenharmony_ci 553d0407baSopenharmony_ci#define OTPC_TIMEOUT 10000 563d0407baSopenharmony_ci 573d0407baSopenharmony_ci#define UDELAY_TWO 2 583d0407baSopenharmony_ci#define UDELAY_FIVE 5 593d0407baSopenharmony_ci 603d0407baSopenharmony_cistruct rockchip_otp { 613d0407baSopenharmony_ci struct device *dev; 623d0407baSopenharmony_ci void __iomem *base; 633d0407baSopenharmony_ci struct clk_bulk_data *clks; 643d0407baSopenharmony_ci int num_clks; 653d0407baSopenharmony_ci struct reset_control *rst; 663d0407baSopenharmony_ci}; 673d0407baSopenharmony_ci 683d0407baSopenharmony_ci/* list of required clocks */ 693d0407baSopenharmony_cistatic const char *const rockchip_otp_clocks[] = { 703d0407baSopenharmony_ci "otp", 713d0407baSopenharmony_ci "apb_pclk", 723d0407baSopenharmony_ci "phy", 733d0407baSopenharmony_ci}; 743d0407baSopenharmony_ci 753d0407baSopenharmony_cistruct rockchip_data { 763d0407baSopenharmony_ci int size; 773d0407baSopenharmony_ci}; 783d0407baSopenharmony_ci 793d0407baSopenharmony_cistatic int rockchip_otp_reset(struct rockchip_otp *otp) 803d0407baSopenharmony_ci{ 813d0407baSopenharmony_ci int ret; 823d0407baSopenharmony_ci 833d0407baSopenharmony_ci ret = reset_control_assert(otp->rst); 843d0407baSopenharmony_ci if (ret) { 853d0407baSopenharmony_ci dev_err(otp->dev, "failed to assert otp phy %d\n", ret); 863d0407baSopenharmony_ci return ret; 873d0407baSopenharmony_ci } 883d0407baSopenharmony_ci 893d0407baSopenharmony_ci udelay(UDELAY_TWO); 903d0407baSopenharmony_ci 913d0407baSopenharmony_ci ret = reset_control_deassert(otp->rst); 923d0407baSopenharmony_ci if (ret) { 933d0407baSopenharmony_ci dev_err(otp->dev, "failed to deassert otp phy %d\n", ret); 943d0407baSopenharmony_ci return ret; 953d0407baSopenharmony_ci } 963d0407baSopenharmony_ci 973d0407baSopenharmony_ci return 0; 983d0407baSopenharmony_ci} 993d0407baSopenharmony_ci 1003d0407baSopenharmony_cistatic int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag) 1013d0407baSopenharmony_ci{ 1023d0407baSopenharmony_ci u32 status = 0; 1033d0407baSopenharmony_ci int ret; 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_ci ret = readl_poll_timeout_atomic(otp->base + OTPC_INT_STATUS, status, (status & flag), 1, OTPC_TIMEOUT); 1063d0407baSopenharmony_ci if (ret) { 1073d0407baSopenharmony_ci return ret; 1083d0407baSopenharmony_ci } 1093d0407baSopenharmony_ci 1103d0407baSopenharmony_ci /* clean int status */ 1113d0407baSopenharmony_ci writel(flag, otp->base + OTPC_INT_STATUS); 1123d0407baSopenharmony_ci 1133d0407baSopenharmony_ci return 0; 1143d0407baSopenharmony_ci} 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_cistatic int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable) 1173d0407baSopenharmony_ci{ 1183d0407baSopenharmony_ci int ret = 0; 1193d0407baSopenharmony_ci 1203d0407baSopenharmony_ci writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), otp->base + OTPC_SBPI_CTRL); 1213d0407baSopenharmony_ci 1223d0407baSopenharmony_ci writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); 1233d0407baSopenharmony_ci writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, otp->base + OTPC_SBPI_CMD0_OFFSET); 1243d0407baSopenharmony_ci if (enable) { 1253d0407baSopenharmony_ci writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 1263d0407baSopenharmony_ci } else { 1273d0407baSopenharmony_ci writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); 1283d0407baSopenharmony_ci } 1293d0407baSopenharmony_ci 1303d0407baSopenharmony_ci writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); 1313d0407baSopenharmony_ci 1323d0407baSopenharmony_ci ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); 1333d0407baSopenharmony_ci if (ret < 0) { 1343d0407baSopenharmony_ci dev_err(otp->dev, "timeout during ecc_enable\n"); 1353d0407baSopenharmony_ci } 1363d0407baSopenharmony_ci 1373d0407baSopenharmony_ci return ret; 1383d0407baSopenharmony_ci} 1393d0407baSopenharmony_ci 1403d0407baSopenharmony_cistatic int rockchip_otp_read(void *context, unsigned int offset, void *val, size_t bytes) 1413d0407baSopenharmony_ci{ 1423d0407baSopenharmony_ci struct rockchip_otp *otp = context; 1433d0407baSopenharmony_ci u8 *buf = val; 1443d0407baSopenharmony_ci int ret = 0; 1453d0407baSopenharmony_ci 1463d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks); 1473d0407baSopenharmony_ci if (ret < 0) { 1483d0407baSopenharmony_ci dev_err(otp->dev, "failed to prepare/enable clks\n"); 1493d0407baSopenharmony_ci return ret; 1503d0407baSopenharmony_ci } 1513d0407baSopenharmony_ci 1523d0407baSopenharmony_ci ret = rockchip_otp_reset(otp); 1533d0407baSopenharmony_ci if (ret) { 1543d0407baSopenharmony_ci dev_err(otp->dev, "failed to reset otp phy\n"); 1553d0407baSopenharmony_ci goto disable_clks; 1563d0407baSopenharmony_ci } 1573d0407baSopenharmony_ci 1583d0407baSopenharmony_ci ret = rockchip_otp_ecc_enable(otp, false); 1593d0407baSopenharmony_ci if (ret < 0) { 1603d0407baSopenharmony_ci dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); 1613d0407baSopenharmony_ci goto disable_clks; 1623d0407baSopenharmony_ci } 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_ci writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 1653d0407baSopenharmony_ci udelay(UDELAY_FIVE); 1663d0407baSopenharmony_ci while (bytes--) { 1673d0407baSopenharmony_ci writel(offset++ | OTPC_USER_ADDR_MASK, otp->base + OTPC_USER_ADDR); 1683d0407baSopenharmony_ci writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, otp->base + OTPC_USER_ENABLE); 1693d0407baSopenharmony_ci ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); 1703d0407baSopenharmony_ci if (ret < 0) { 1713d0407baSopenharmony_ci dev_err(otp->dev, "timeout during read setup\n"); 1723d0407baSopenharmony_ci goto read_end; 1733d0407baSopenharmony_ci } 1743d0407baSopenharmony_ci *buf++ = readb(otp->base + OTPC_USER_Q); 1753d0407baSopenharmony_ci } 1763d0407baSopenharmony_ci 1773d0407baSopenharmony_ciread_end: 1783d0407baSopenharmony_ci writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); 1793d0407baSopenharmony_cidisable_clks: 1803d0407baSopenharmony_ci clk_bulk_disable_unprepare(otp->num_clks, otp->clks); 1813d0407baSopenharmony_ci 1823d0407baSopenharmony_ci return ret; 1833d0407baSopenharmony_ci} 1843d0407baSopenharmony_ci 1853d0407baSopenharmony_cistatic struct nvmem_config otp_config = { 1863d0407baSopenharmony_ci .name = "rockchip-otp", 1873d0407baSopenharmony_ci .owner = THIS_MODULE, 1883d0407baSopenharmony_ci .read_only = true, 1893d0407baSopenharmony_ci .stride = 1, 1903d0407baSopenharmony_ci .word_size = 1, 1913d0407baSopenharmony_ci .reg_read = rockchip_otp_read, 1923d0407baSopenharmony_ci}; 1933d0407baSopenharmony_ci 1943d0407baSopenharmony_cistatic const struct rockchip_data px30_data = { 1953d0407baSopenharmony_ci .size = 0x40, 1963d0407baSopenharmony_ci}; 1973d0407baSopenharmony_ci 1983d0407baSopenharmony_cistatic const struct of_device_id rockchip_otp_match[] = { 1993d0407baSopenharmony_ci { 2003d0407baSopenharmony_ci .compatible = "rockchip,px30-otp", 2013d0407baSopenharmony_ci .data = (void *)&px30_data, 2023d0407baSopenharmony_ci }, 2033d0407baSopenharmony_ci { 2043d0407baSopenharmony_ci .compatible = "rockchip,rk3308-otp", 2053d0407baSopenharmony_ci .data = (void *)&px30_data, 2063d0407baSopenharmony_ci }, 2073d0407baSopenharmony_ci {}, 2083d0407baSopenharmony_ci}; 2093d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_otp_match); 2103d0407baSopenharmony_ci 2113d0407baSopenharmony_cistatic int rockchip_otp_probe(struct platform_device *pdev) 2123d0407baSopenharmony_ci{ 2133d0407baSopenharmony_ci struct device *dev = &pdev->dev; 2143d0407baSopenharmony_ci struct rockchip_otp *otp; 2153d0407baSopenharmony_ci const struct rockchip_data *data; 2163d0407baSopenharmony_ci struct nvmem_device *nvmem; 2173d0407baSopenharmony_ci int ret, i; 2183d0407baSopenharmony_ci 2193d0407baSopenharmony_ci data = of_device_get_match_data(dev); 2203d0407baSopenharmony_ci if (!data) { 2213d0407baSopenharmony_ci dev_err(dev, "failed to get match data\n"); 2223d0407baSopenharmony_ci return -EINVAL; 2233d0407baSopenharmony_ci } 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), GFP_KERNEL); 2263d0407baSopenharmony_ci if (!otp) { 2273d0407baSopenharmony_ci return -ENOMEM; 2283d0407baSopenharmony_ci } 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci otp->dev = dev; 2313d0407baSopenharmony_ci otp->base = devm_platform_ioremap_resource(pdev, 0); 2323d0407baSopenharmony_ci if (IS_ERR(otp->base)) { 2333d0407baSopenharmony_ci return PTR_ERR(otp->base); 2343d0407baSopenharmony_ci } 2353d0407baSopenharmony_ci 2363d0407baSopenharmony_ci otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks); 2373d0407baSopenharmony_ci otp->clks = devm_kcalloc(dev, otp->num_clks, sizeof(*otp->clks), GFP_KERNEL); 2383d0407baSopenharmony_ci if (!otp->clks) { 2393d0407baSopenharmony_ci return -ENOMEM; 2403d0407baSopenharmony_ci } 2413d0407baSopenharmony_ci 2423d0407baSopenharmony_ci for (i = 0; i < otp->num_clks; ++i) { 2433d0407baSopenharmony_ci otp->clks[i].id = rockchip_otp_clocks[i]; 2443d0407baSopenharmony_ci } 2453d0407baSopenharmony_ci 2463d0407baSopenharmony_ci ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks); 2473d0407baSopenharmony_ci if (ret) { 2483d0407baSopenharmony_ci return ret; 2493d0407baSopenharmony_ci } 2503d0407baSopenharmony_ci 2513d0407baSopenharmony_ci otp->rst = devm_reset_control_get(dev, "phy"); 2523d0407baSopenharmony_ci if (IS_ERR(otp->rst)) { 2533d0407baSopenharmony_ci return PTR_ERR(otp->rst); 2543d0407baSopenharmony_ci } 2553d0407baSopenharmony_ci 2563d0407baSopenharmony_ci otp_config.size = data->size; 2573d0407baSopenharmony_ci otp_config.priv = otp; 2583d0407baSopenharmony_ci otp_config.dev = dev; 2593d0407baSopenharmony_ci nvmem = devm_nvmem_register(dev, &otp_config); 2603d0407baSopenharmony_ci 2613d0407baSopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 2623d0407baSopenharmony_ci} 2633d0407baSopenharmony_ci 2643d0407baSopenharmony_cistatic struct platform_driver rockchip_otp_driver = { 2653d0407baSopenharmony_ci .probe = rockchip_otp_probe, 2663d0407baSopenharmony_ci .driver = 2673d0407baSopenharmony_ci { 2683d0407baSopenharmony_ci .name = "rockchip-otp", 2693d0407baSopenharmony_ci .of_match_table = rockchip_otp_match, 2703d0407baSopenharmony_ci }, 2713d0407baSopenharmony_ci}; 2723d0407baSopenharmony_ci 2733d0407baSopenharmony_cistatic int __init rockchip_otp_init(void) 2743d0407baSopenharmony_ci{ 2753d0407baSopenharmony_ci int ret; 2763d0407baSopenharmony_ci 2773d0407baSopenharmony_ci ret = platform_driver_register(&rockchip_otp_driver); 2783d0407baSopenharmony_ci if (ret) { 2793d0407baSopenharmony_ci pr_err("failed to register otp driver\n"); 2803d0407baSopenharmony_ci return ret; 2813d0407baSopenharmony_ci } 2823d0407baSopenharmony_ci 2833d0407baSopenharmony_ci return 0; 2843d0407baSopenharmony_ci} 2853d0407baSopenharmony_ci 2863d0407baSopenharmony_cistatic void __exit rockchip_otp_exit(void) 2873d0407baSopenharmony_ci{ 2883d0407baSopenharmony_ci return platform_driver_unregister(&rockchip_otp_driver); 2893d0407baSopenharmony_ci} 2903d0407baSopenharmony_ci 2913d0407baSopenharmony_cisubsys_initcall(rockchip_otp_init); 2923d0407baSopenharmony_cimodule_exit(rockchip_otp_exit); 2933d0407baSopenharmony_ci 2943d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip OTP driver"); 2953d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 296