162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Rockchip eFuse Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Rockchip Electronics Co. Ltd. 662306a36Sopenharmony_ci * Author: Caesar Wang <wxt@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/module.h> 1462306a36Sopenharmony_ci#include <linux/nvmem-provider.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_platform.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define RK3288_A_SHIFT 6 2162306a36Sopenharmony_ci#define RK3288_A_MASK 0x3ff 2262306a36Sopenharmony_ci#define RK3288_PGENB BIT(3) 2362306a36Sopenharmony_ci#define RK3288_LOAD BIT(2) 2462306a36Sopenharmony_ci#define RK3288_STROBE BIT(1) 2562306a36Sopenharmony_ci#define RK3288_CSB BIT(0) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define RK3328_SECURE_SIZES 96 2862306a36Sopenharmony_ci#define RK3328_INT_STATUS 0x0018 2962306a36Sopenharmony_ci#define RK3328_DOUT 0x0020 3062306a36Sopenharmony_ci#define RK3328_AUTO_CTRL 0x0024 3162306a36Sopenharmony_ci#define RK3328_INT_FINISH BIT(0) 3262306a36Sopenharmony_ci#define RK3328_AUTO_ENB BIT(0) 3362306a36Sopenharmony_ci#define RK3328_AUTO_RD BIT(1) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define RK3399_A_SHIFT 16 3662306a36Sopenharmony_ci#define RK3399_A_MASK 0x3ff 3762306a36Sopenharmony_ci#define RK3399_NBYTES 4 3862306a36Sopenharmony_ci#define RK3399_STROBSFTSEL BIT(9) 3962306a36Sopenharmony_ci#define RK3399_RSB BIT(7) 4062306a36Sopenharmony_ci#define RK3399_PD BIT(5) 4162306a36Sopenharmony_ci#define RK3399_PGENB BIT(3) 4262306a36Sopenharmony_ci#define RK3399_LOAD BIT(2) 4362306a36Sopenharmony_ci#define RK3399_STROBE BIT(1) 4462306a36Sopenharmony_ci#define RK3399_CSB BIT(0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define REG_EFUSE_CTRL 0x0000 4762306a36Sopenharmony_ci#define REG_EFUSE_DOUT 0x0004 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct rockchip_efuse_chip { 5062306a36Sopenharmony_ci struct device *dev; 5162306a36Sopenharmony_ci void __iomem *base; 5262306a36Sopenharmony_ci struct clk *clk; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int rockchip_rk3288_efuse_read(void *context, unsigned int offset, 5662306a36Sopenharmony_ci void *val, size_t bytes) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct rockchip_efuse_chip *efuse = context; 5962306a36Sopenharmony_ci u8 *buf = val; 6062306a36Sopenharmony_ci int ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = clk_prepare_enable(efuse->clk); 6362306a36Sopenharmony_ci if (ret < 0) { 6462306a36Sopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); 6962306a36Sopenharmony_ci udelay(1); 7062306a36Sopenharmony_ci while (bytes--) { 7162306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & 7262306a36Sopenharmony_ci (~(RK3288_A_MASK << RK3288_A_SHIFT)), 7362306a36Sopenharmony_ci efuse->base + REG_EFUSE_CTRL); 7462306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | 7562306a36Sopenharmony_ci ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 7662306a36Sopenharmony_ci efuse->base + REG_EFUSE_CTRL); 7762306a36Sopenharmony_ci udelay(1); 7862306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | 7962306a36Sopenharmony_ci RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); 8062306a36Sopenharmony_ci udelay(1); 8162306a36Sopenharmony_ci *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 8262306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & 8362306a36Sopenharmony_ci (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); 8462306a36Sopenharmony_ci udelay(1); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Switch to standby mode */ 8862306a36Sopenharmony_ci writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci clk_disable_unprepare(efuse->clk); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int rockchip_rk3328_efuse_read(void *context, unsigned int offset, 9662306a36Sopenharmony_ci void *val, size_t bytes) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct rockchip_efuse_chip *efuse = context; 9962306a36Sopenharmony_ci unsigned int addr_start, addr_end, addr_offset, addr_len; 10062306a36Sopenharmony_ci u32 out_value, status; 10162306a36Sopenharmony_ci u8 *buf; 10262306a36Sopenharmony_ci int ret, i = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = clk_prepare_enable(efuse->clk); 10562306a36Sopenharmony_ci if (ret < 0) { 10662306a36Sopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 11162306a36Sopenharmony_ci offset += RK3328_SECURE_SIZES; 11262306a36Sopenharmony_ci addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 11362306a36Sopenharmony_ci addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 11462306a36Sopenharmony_ci addr_offset = offset % RK3399_NBYTES; 11562306a36Sopenharmony_ci addr_len = addr_end - addr_start; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), 11862306a36Sopenharmony_ci GFP_KERNEL); 11962306a36Sopenharmony_ci if (!buf) { 12062306a36Sopenharmony_ci ret = -ENOMEM; 12162306a36Sopenharmony_ci goto nomem; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci while (addr_len--) { 12562306a36Sopenharmony_ci writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | 12662306a36Sopenharmony_ci ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 12762306a36Sopenharmony_ci efuse->base + RK3328_AUTO_CTRL); 12862306a36Sopenharmony_ci udelay(4); 12962306a36Sopenharmony_ci status = readl(efuse->base + RK3328_INT_STATUS); 13062306a36Sopenharmony_ci if (!(status & RK3328_INT_FINISH)) { 13162306a36Sopenharmony_ci ret = -EIO; 13262306a36Sopenharmony_ci goto err; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci out_value = readl(efuse->base + RK3328_DOUT); 13562306a36Sopenharmony_ci writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci memcpy(&buf[i], &out_value, RK3399_NBYTES); 13862306a36Sopenharmony_ci i += RK3399_NBYTES; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci memcpy(val, buf + addr_offset, bytes); 14262306a36Sopenharmony_cierr: 14362306a36Sopenharmony_ci kfree(buf); 14462306a36Sopenharmony_cinomem: 14562306a36Sopenharmony_ci clk_disable_unprepare(efuse->clk); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int rockchip_rk3399_efuse_read(void *context, unsigned int offset, 15162306a36Sopenharmony_ci void *val, size_t bytes) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct rockchip_efuse_chip *efuse = context; 15462306a36Sopenharmony_ci unsigned int addr_start, addr_end, addr_offset, addr_len; 15562306a36Sopenharmony_ci u32 out_value; 15662306a36Sopenharmony_ci u8 *buf; 15762306a36Sopenharmony_ci int ret, i = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = clk_prepare_enable(efuse->clk); 16062306a36Sopenharmony_ci if (ret < 0) { 16162306a36Sopenharmony_ci dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 16662306a36Sopenharmony_ci addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 16762306a36Sopenharmony_ci addr_offset = offset % RK3399_NBYTES; 16862306a36Sopenharmony_ci addr_len = addr_end - addr_start; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), 17162306a36Sopenharmony_ci GFP_KERNEL); 17262306a36Sopenharmony_ci if (!buf) { 17362306a36Sopenharmony_ci clk_disable_unprepare(efuse->clk); 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 17862306a36Sopenharmony_ci efuse->base + REG_EFUSE_CTRL); 17962306a36Sopenharmony_ci udelay(1); 18062306a36Sopenharmony_ci while (addr_len--) { 18162306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | 18262306a36Sopenharmony_ci ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 18362306a36Sopenharmony_ci efuse->base + REG_EFUSE_CTRL); 18462306a36Sopenharmony_ci udelay(1); 18562306a36Sopenharmony_ci out_value = readl(efuse->base + REG_EFUSE_DOUT); 18662306a36Sopenharmony_ci writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), 18762306a36Sopenharmony_ci efuse->base + REG_EFUSE_CTRL); 18862306a36Sopenharmony_ci udelay(1); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci memcpy(&buf[i], &out_value, RK3399_NBYTES); 19162306a36Sopenharmony_ci i += RK3399_NBYTES; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Switch to standby mode */ 19562306a36Sopenharmony_ci writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci memcpy(val, buf + addr_offset, bytes); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci kfree(buf); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci clk_disable_unprepare(efuse->clk); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct nvmem_config econfig = { 20762306a36Sopenharmony_ci .name = "rockchip-efuse", 20862306a36Sopenharmony_ci .stride = 1, 20962306a36Sopenharmony_ci .word_size = 1, 21062306a36Sopenharmony_ci .read_only = true, 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const struct of_device_id rockchip_efuse_match[] = { 21462306a36Sopenharmony_ci /* deprecated but kept around for dts binding compatibility */ 21562306a36Sopenharmony_ci { 21662306a36Sopenharmony_ci .compatible = "rockchip,rockchip-efuse", 21762306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci .compatible = "rockchip,rk3066a-efuse", 22162306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 22262306a36Sopenharmony_ci }, 22362306a36Sopenharmony_ci { 22462306a36Sopenharmony_ci .compatible = "rockchip,rk3188-efuse", 22562306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 22662306a36Sopenharmony_ci }, 22762306a36Sopenharmony_ci { 22862306a36Sopenharmony_ci .compatible = "rockchip,rk3228-efuse", 22962306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 23062306a36Sopenharmony_ci }, 23162306a36Sopenharmony_ci { 23262306a36Sopenharmony_ci .compatible = "rockchip,rk3288-efuse", 23362306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci { 23662306a36Sopenharmony_ci .compatible = "rockchip,rk3368-efuse", 23762306a36Sopenharmony_ci .data = (void *)&rockchip_rk3288_efuse_read, 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci { 24062306a36Sopenharmony_ci .compatible = "rockchip,rk3328-efuse", 24162306a36Sopenharmony_ci .data = (void *)&rockchip_rk3328_efuse_read, 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci { 24462306a36Sopenharmony_ci .compatible = "rockchip,rk3399-efuse", 24562306a36Sopenharmony_ci .data = (void *)&rockchip_rk3399_efuse_read, 24662306a36Sopenharmony_ci }, 24762306a36Sopenharmony_ci { /* sentinel */}, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_efuse_match); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int rockchip_efuse_probe(struct platform_device *pdev) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct resource *res; 25462306a36Sopenharmony_ci struct nvmem_device *nvmem; 25562306a36Sopenharmony_ci struct rockchip_efuse_chip *efuse; 25662306a36Sopenharmony_ci const void *data; 25762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci data = of_device_get_match_data(dev); 26062306a36Sopenharmony_ci if (!data) { 26162306a36Sopenharmony_ci dev_err(dev, "failed to get match data\n"); 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip), 26662306a36Sopenharmony_ci GFP_KERNEL); 26762306a36Sopenharmony_ci if (!efuse) 26862306a36Sopenharmony_ci return -ENOMEM; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci efuse->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 27162306a36Sopenharmony_ci if (IS_ERR(efuse->base)) 27262306a36Sopenharmony_ci return PTR_ERR(efuse->base); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci efuse->clk = devm_clk_get(dev, "pclk_efuse"); 27562306a36Sopenharmony_ci if (IS_ERR(efuse->clk)) 27662306a36Sopenharmony_ci return PTR_ERR(efuse->clk); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci efuse->dev = dev; 27962306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", 28062306a36Sopenharmony_ci &econfig.size)) 28162306a36Sopenharmony_ci econfig.size = resource_size(res); 28262306a36Sopenharmony_ci econfig.reg_read = data; 28362306a36Sopenharmony_ci econfig.priv = efuse; 28462306a36Sopenharmony_ci econfig.dev = efuse->dev; 28562306a36Sopenharmony_ci nvmem = devm_nvmem_register(dev, &econfig); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic struct platform_driver rockchip_efuse_driver = { 29162306a36Sopenharmony_ci .probe = rockchip_efuse_probe, 29262306a36Sopenharmony_ci .driver = { 29362306a36Sopenharmony_ci .name = "rockchip-efuse", 29462306a36Sopenharmony_ci .of_match_table = rockchip_efuse_match, 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cimodule_platform_driver(rockchip_efuse_driver); 29962306a36Sopenharmony_ciMODULE_DESCRIPTION("rockchip_efuse driver"); 30062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 301