103a69568SZhengShunQian /* 203a69568SZhengShunQian * Rockchip eFuse Driver 303a69568SZhengShunQian * 403a69568SZhengShunQian * Copyright (c) 2015 Rockchip Electronics Co. Ltd. 503a69568SZhengShunQian * Author: Caesar Wang <wxt@rock-chips.com> 603a69568SZhengShunQian * 703a69568SZhengShunQian * This program is free software; you can redistribute it and/or modify it 803a69568SZhengShunQian * under the terms of version 2 of the GNU General Public License as 903a69568SZhengShunQian * published by the Free Software Foundation. 1003a69568SZhengShunQian * 1103a69568SZhengShunQian * This program is distributed in the hope that it will be useful, but WITHOUT 1203a69568SZhengShunQian * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1303a69568SZhengShunQian * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1403a69568SZhengShunQian * more details. 1503a69568SZhengShunQian */ 1603a69568SZhengShunQian 17c37ff3fbSCaesar Wang #include <linux/clk.h> 18c37ff3fbSCaesar Wang #include <linux/delay.h> 1903a69568SZhengShunQian #include <linux/device.h> 2003a69568SZhengShunQian #include <linux/io.h> 2103a69568SZhengShunQian #include <linux/module.h> 22c37ff3fbSCaesar Wang #include <linux/nvmem-provider.h> 23c37ff3fbSCaesar Wang #include <linux/slab.h> 2403a69568SZhengShunQian #include <linux/of.h> 2502baff32SFinley Xiao #include <linux/of_platform.h> 26c37ff3fbSCaesar Wang #include <linux/platform_device.h> 2703a69568SZhengShunQian 2802baff32SFinley Xiao #define RK3288_A_SHIFT 6 2902baff32SFinley Xiao #define RK3288_A_MASK 0x3ff 3002baff32SFinley Xiao #define RK3288_PGENB BIT(3) 3102baff32SFinley Xiao #define RK3288_LOAD BIT(2) 3202baff32SFinley Xiao #define RK3288_STROBE BIT(1) 3302baff32SFinley Xiao #define RK3288_CSB BIT(0) 3402baff32SFinley Xiao 359a479b08SFinley Xiao #define RK3328_SECURE_SIZES 96 369a479b08SFinley Xiao #define RK3328_INT_STATUS 0x0018 379a479b08SFinley Xiao #define RK3328_DOUT 0x0020 389a479b08SFinley Xiao #define RK3328_AUTO_CTRL 0x0024 399a479b08SFinley Xiao #define RK3328_INT_FINISH BIT(0) 409a479b08SFinley Xiao #define RK3328_AUTO_ENB BIT(0) 419a479b08SFinley Xiao #define RK3328_AUTO_RD BIT(1) 429a479b08SFinley Xiao 4302baff32SFinley Xiao #define RK3399_A_SHIFT 16 4402baff32SFinley Xiao #define RK3399_A_MASK 0x3ff 4502baff32SFinley Xiao #define RK3399_NBYTES 4 4602baff32SFinley Xiao #define RK3399_STROBSFTSEL BIT(9) 4702baff32SFinley Xiao #define RK3399_RSB BIT(7) 4802baff32SFinley Xiao #define RK3399_PD BIT(5) 4902baff32SFinley Xiao #define RK3399_PGENB BIT(3) 5002baff32SFinley Xiao #define RK3399_LOAD BIT(2) 5102baff32SFinley Xiao #define RK3399_STROBE BIT(1) 5202baff32SFinley Xiao #define RK3399_CSB BIT(0) 5303a69568SZhengShunQian 5403a69568SZhengShunQian #define REG_EFUSE_CTRL 0x0000 5503a69568SZhengShunQian #define REG_EFUSE_DOUT 0x0004 5603a69568SZhengShunQian 57c37ff3fbSCaesar Wang struct rockchip_efuse_chip { 5803a69568SZhengShunQian struct device *dev; 5903a69568SZhengShunQian void __iomem *base; 60c37ff3fbSCaesar Wang struct clk *clk; 6103a69568SZhengShunQian }; 6203a69568SZhengShunQian 6302baff32SFinley Xiao static int rockchip_rk3288_efuse_read(void *context, unsigned int offset, 64cc907553SSrinivas Kandagatla void *val, size_t bytes) 6503a69568SZhengShunQian { 66c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse = context; 6703a69568SZhengShunQian u8 *buf = val; 6803a69568SZhengShunQian int ret; 6903a69568SZhengShunQian 70c37ff3fbSCaesar Wang ret = clk_prepare_enable(efuse->clk); 7103a69568SZhengShunQian if (ret < 0) { 72c37ff3fbSCaesar Wang dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 7303a69568SZhengShunQian return ret; 7403a69568SZhengShunQian } 7503a69568SZhengShunQian 7602baff32SFinley Xiao writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); 7703a69568SZhengShunQian udelay(1); 78cc907553SSrinivas Kandagatla while (bytes--) { 79c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 8002baff32SFinley Xiao (~(RK3288_A_MASK << RK3288_A_SHIFT)), 81c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 82c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 8302baff32SFinley Xiao ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 84c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 8503a69568SZhengShunQian udelay(1); 86c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 8702baff32SFinley Xiao RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); 8803a69568SZhengShunQian udelay(1); 89c37ff3fbSCaesar Wang *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 90c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 9102baff32SFinley Xiao (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); 9203a69568SZhengShunQian udelay(1); 9303a69568SZhengShunQian } 9403a69568SZhengShunQian 9503a69568SZhengShunQian /* Switch to standby mode */ 9602baff32SFinley Xiao writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); 9702baff32SFinley Xiao 9802baff32SFinley Xiao clk_disable_unprepare(efuse->clk); 9902baff32SFinley Xiao 10002baff32SFinley Xiao return 0; 10102baff32SFinley Xiao } 10202baff32SFinley Xiao 1039a479b08SFinley Xiao static int rockchip_rk3328_efuse_read(void *context, unsigned int offset, 1049a479b08SFinley Xiao void *val, size_t bytes) 1059a479b08SFinley Xiao { 1069a479b08SFinley Xiao struct rockchip_efuse_chip *efuse = context; 1079a479b08SFinley Xiao unsigned int addr_start, addr_end, addr_offset, addr_len; 1089a479b08SFinley Xiao u32 out_value, status; 1099a479b08SFinley Xiao u8 *buf; 1109a479b08SFinley Xiao int ret, i = 0; 1119a479b08SFinley Xiao 1129a479b08SFinley Xiao ret = clk_prepare_enable(efuse->clk); 1139a479b08SFinley Xiao if (ret < 0) { 1149a479b08SFinley Xiao dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 1159a479b08SFinley Xiao return ret; 1169a479b08SFinley Xiao } 1179a479b08SFinley Xiao 1189a479b08SFinley Xiao /* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */ 1199a479b08SFinley Xiao offset += RK3328_SECURE_SIZES; 1209a479b08SFinley Xiao addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 1219a479b08SFinley Xiao addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 1229a479b08SFinley Xiao addr_offset = offset % RK3399_NBYTES; 1239a479b08SFinley Xiao addr_len = addr_end - addr_start; 1249a479b08SFinley Xiao 125*6396bb22SKees Cook buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), 126*6396bb22SKees Cook GFP_KERNEL); 1279a479b08SFinley Xiao if (!buf) { 1289a479b08SFinley Xiao ret = -ENOMEM; 1299a479b08SFinley Xiao goto nomem; 1309a479b08SFinley Xiao } 1319a479b08SFinley Xiao 1329a479b08SFinley Xiao while (addr_len--) { 1339a479b08SFinley Xiao writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | 1349a479b08SFinley Xiao ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 1359a479b08SFinley Xiao efuse->base + RK3328_AUTO_CTRL); 1369a479b08SFinley Xiao udelay(4); 1379a479b08SFinley Xiao status = readl(efuse->base + RK3328_INT_STATUS); 1389a479b08SFinley Xiao if (!(status & RK3328_INT_FINISH)) { 1399a479b08SFinley Xiao ret = -EIO; 1409a479b08SFinley Xiao goto err; 1419a479b08SFinley Xiao } 1429a479b08SFinley Xiao out_value = readl(efuse->base + RK3328_DOUT); 1439a479b08SFinley Xiao writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS); 1449a479b08SFinley Xiao 1459a479b08SFinley Xiao memcpy(&buf[i], &out_value, RK3399_NBYTES); 1469a479b08SFinley Xiao i += RK3399_NBYTES; 1479a479b08SFinley Xiao } 1489a479b08SFinley Xiao 1499a479b08SFinley Xiao memcpy(val, buf + addr_offset, bytes); 1509a479b08SFinley Xiao err: 1519a479b08SFinley Xiao kfree(buf); 1529a479b08SFinley Xiao nomem: 1539a479b08SFinley Xiao clk_disable_unprepare(efuse->clk); 1549a479b08SFinley Xiao 1559a479b08SFinley Xiao return ret; 1569a479b08SFinley Xiao } 1579a479b08SFinley Xiao 15802baff32SFinley Xiao static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, 15902baff32SFinley Xiao void *val, size_t bytes) 16002baff32SFinley Xiao { 16102baff32SFinley Xiao struct rockchip_efuse_chip *efuse = context; 16202baff32SFinley Xiao unsigned int addr_start, addr_end, addr_offset, addr_len; 16302baff32SFinley Xiao u32 out_value; 16402baff32SFinley Xiao u8 *buf; 16502baff32SFinley Xiao int ret, i = 0; 16602baff32SFinley Xiao 16702baff32SFinley Xiao ret = clk_prepare_enable(efuse->clk); 16802baff32SFinley Xiao if (ret < 0) { 16902baff32SFinley Xiao dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 17002baff32SFinley Xiao return ret; 17102baff32SFinley Xiao } 17202baff32SFinley Xiao 17302baff32SFinley Xiao addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 17402baff32SFinley Xiao addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 17502baff32SFinley Xiao addr_offset = offset % RK3399_NBYTES; 17602baff32SFinley Xiao addr_len = addr_end - addr_start; 17702baff32SFinley Xiao 178*6396bb22SKees Cook buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), 179*6396bb22SKees Cook GFP_KERNEL); 18002baff32SFinley Xiao if (!buf) { 18102baff32SFinley Xiao clk_disable_unprepare(efuse->clk); 18202baff32SFinley Xiao return -ENOMEM; 18302baff32SFinley Xiao } 18402baff32SFinley Xiao 18502baff32SFinley Xiao writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 18602baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 18702baff32SFinley Xiao udelay(1); 18802baff32SFinley Xiao while (addr_len--) { 18902baff32SFinley Xiao writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | 19002baff32SFinley Xiao ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 19102baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 19202baff32SFinley Xiao udelay(1); 19302baff32SFinley Xiao out_value = readl(efuse->base + REG_EFUSE_DOUT); 19402baff32SFinley Xiao writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), 19502baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 19602baff32SFinley Xiao udelay(1); 19702baff32SFinley Xiao 19802baff32SFinley Xiao memcpy(&buf[i], &out_value, RK3399_NBYTES); 19902baff32SFinley Xiao i += RK3399_NBYTES; 20002baff32SFinley Xiao } 20102baff32SFinley Xiao 20202baff32SFinley Xiao /* Switch to standby mode */ 20302baff32SFinley Xiao writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); 20402baff32SFinley Xiao 20502baff32SFinley Xiao memcpy(val, buf + addr_offset, bytes); 20602baff32SFinley Xiao 20702baff32SFinley Xiao kfree(buf); 20803a69568SZhengShunQian 209c37ff3fbSCaesar Wang clk_disable_unprepare(efuse->clk); 21003a69568SZhengShunQian 21103a69568SZhengShunQian return 0; 21203a69568SZhengShunQian } 21303a69568SZhengShunQian 21403a69568SZhengShunQian static struct nvmem_config econfig = { 21503a69568SZhengShunQian .name = "rockchip-efuse", 216cc907553SSrinivas Kandagatla .stride = 1, 217cc907553SSrinivas Kandagatla .word_size = 1, 21803a69568SZhengShunQian .read_only = true, 21903a69568SZhengShunQian }; 22003a69568SZhengShunQian 22103a69568SZhengShunQian static const struct of_device_id rockchip_efuse_match[] = { 22202baff32SFinley Xiao /* deprecated but kept around for dts binding compatibility */ 22302baff32SFinley Xiao { 22402baff32SFinley Xiao .compatible = "rockchip,rockchip-efuse", 22502baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 22602baff32SFinley Xiao }, 22702baff32SFinley Xiao { 22802baff32SFinley Xiao .compatible = "rockchip,rk3066a-efuse", 22902baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 23002baff32SFinley Xiao }, 23102baff32SFinley Xiao { 23202baff32SFinley Xiao .compatible = "rockchip,rk3188-efuse", 23302baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 23402baff32SFinley Xiao }, 23502baff32SFinley Xiao { 236d6e4bd1bSFrank Wang .compatible = "rockchip,rk3228-efuse", 237820de1fbSFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 238820de1fbSFinley Xiao }, 239820de1fbSFinley Xiao { 24002baff32SFinley Xiao .compatible = "rockchip,rk3288-efuse", 24102baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 24202baff32SFinley Xiao }, 24302baff32SFinley Xiao { 2447a15cf2aSRomain Perier .compatible = "rockchip,rk3368-efuse", 2457a15cf2aSRomain Perier .data = (void *)&rockchip_rk3288_efuse_read, 2467a15cf2aSRomain Perier }, 2477a15cf2aSRomain Perier { 2489a479b08SFinley Xiao .compatible = "rockchip,rk3328-efuse", 2499a479b08SFinley Xiao .data = (void *)&rockchip_rk3328_efuse_read, 2509a479b08SFinley Xiao }, 2519a479b08SFinley Xiao { 25202baff32SFinley Xiao .compatible = "rockchip,rk3399-efuse", 25302baff32SFinley Xiao .data = (void *)&rockchip_rk3399_efuse_read, 25402baff32SFinley Xiao }, 25503a69568SZhengShunQian { /* sentinel */}, 25603a69568SZhengShunQian }; 25703a69568SZhengShunQian MODULE_DEVICE_TABLE(of, rockchip_efuse_match); 25803a69568SZhengShunQian 2597e532f79Skbuild test robot static int rockchip_efuse_probe(struct platform_device *pdev) 26003a69568SZhengShunQian { 26103a69568SZhengShunQian struct resource *res; 26203a69568SZhengShunQian struct nvmem_device *nvmem; 263c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse; 2647b4e76cbSAndrey Smirnov const void *data; 26502baff32SFinley Xiao struct device *dev = &pdev->dev; 26602baff32SFinley Xiao 2677b4e76cbSAndrey Smirnov data = of_device_get_match_data(dev); 2687b4e76cbSAndrey Smirnov if (!data) { 26902baff32SFinley Xiao dev_err(dev, "failed to get match data\n"); 27002baff32SFinley Xiao return -EINVAL; 27102baff32SFinley Xiao } 272c37ff3fbSCaesar Wang 273e84d1f96SAndrey Smirnov efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip), 274c37ff3fbSCaesar Wang GFP_KERNEL); 275c37ff3fbSCaesar Wang if (!efuse) 276c37ff3fbSCaesar Wang return -ENOMEM; 27703a69568SZhengShunQian 27803a69568SZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 279e84d1f96SAndrey Smirnov efuse->base = devm_ioremap_resource(dev, res); 280c37ff3fbSCaesar Wang if (IS_ERR(efuse->base)) 281c37ff3fbSCaesar Wang return PTR_ERR(efuse->base); 28203a69568SZhengShunQian 283e84d1f96SAndrey Smirnov efuse->clk = devm_clk_get(dev, "pclk_efuse"); 284c37ff3fbSCaesar Wang if (IS_ERR(efuse->clk)) 285c37ff3fbSCaesar Wang return PTR_ERR(efuse->clk); 28603a69568SZhengShunQian 287e84d1f96SAndrey Smirnov efuse->dev = dev; 28832277723SFinley Xiao if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", 28932277723SFinley Xiao &econfig.size)) 290cc907553SSrinivas Kandagatla econfig.size = resource_size(res); 2917b4e76cbSAndrey Smirnov econfig.reg_read = data; 292cc907553SSrinivas Kandagatla econfig.priv = efuse; 293c37ff3fbSCaesar Wang econfig.dev = efuse->dev; 294f4bec713SAndrey Smirnov nvmem = devm_nvmem_register(dev, &econfig); 29503a69568SZhengShunQian 296f4bec713SAndrey Smirnov return PTR_ERR_OR_ZERO(nvmem); 29703a69568SZhengShunQian } 29803a69568SZhengShunQian 29903a69568SZhengShunQian static struct platform_driver rockchip_efuse_driver = { 30003a69568SZhengShunQian .probe = rockchip_efuse_probe, 30103a69568SZhengShunQian .driver = { 30203a69568SZhengShunQian .name = "rockchip-efuse", 30303a69568SZhengShunQian .of_match_table = rockchip_efuse_match, 30403a69568SZhengShunQian }, 30503a69568SZhengShunQian }; 30603a69568SZhengShunQian 30703a69568SZhengShunQian module_platform_driver(rockchip_efuse_driver); 30803a69568SZhengShunQian MODULE_DESCRIPTION("rockchip_efuse driver"); 30903a69568SZhengShunQian MODULE_LICENSE("GPL v2"); 310