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 17*c37ff3fbSCaesar Wang #include <linux/clk.h> 18*c37ff3fbSCaesar Wang #include <linux/delay.h> 1903a69568SZhengShunQian #include <linux/device.h> 2003a69568SZhengShunQian #include <linux/io.h> 2103a69568SZhengShunQian #include <linux/module.h> 22*c37ff3fbSCaesar Wang #include <linux/nvmem-provider.h> 23*c37ff3fbSCaesar Wang #include <linux/slab.h> 2403a69568SZhengShunQian #include <linux/of.h> 25*c37ff3fbSCaesar Wang #include <linux/platform_device.h> 26*c37ff3fbSCaesar Wang #include <linux/regmap.h> 2703a69568SZhengShunQian 2803a69568SZhengShunQian #define EFUSE_A_SHIFT 6 2903a69568SZhengShunQian #define EFUSE_A_MASK 0x3ff 3003a69568SZhengShunQian #define EFUSE_PGENB BIT(3) 3103a69568SZhengShunQian #define EFUSE_LOAD BIT(2) 3203a69568SZhengShunQian #define EFUSE_STROBE BIT(1) 3303a69568SZhengShunQian #define EFUSE_CSB BIT(0) 3403a69568SZhengShunQian 3503a69568SZhengShunQian #define REG_EFUSE_CTRL 0x0000 3603a69568SZhengShunQian #define REG_EFUSE_DOUT 0x0004 3703a69568SZhengShunQian 38*c37ff3fbSCaesar Wang struct rockchip_efuse_chip { 3903a69568SZhengShunQian struct device *dev; 4003a69568SZhengShunQian void __iomem *base; 41*c37ff3fbSCaesar Wang struct clk *clk; 4203a69568SZhengShunQian }; 4303a69568SZhengShunQian 4403a69568SZhengShunQian static int rockchip_efuse_write(void *context, const void *data, size_t count) 4503a69568SZhengShunQian { 4603a69568SZhengShunQian /* Nothing TBD, Read-Only */ 4703a69568SZhengShunQian return 0; 4803a69568SZhengShunQian } 4903a69568SZhengShunQian 5003a69568SZhengShunQian static int rockchip_efuse_read(void *context, 5103a69568SZhengShunQian const void *reg, size_t reg_size, 5203a69568SZhengShunQian void *val, size_t val_size) 5303a69568SZhengShunQian { 5403a69568SZhengShunQian unsigned int offset = *(u32 *)reg; 55*c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse = context; 5603a69568SZhengShunQian u8 *buf = val; 5703a69568SZhengShunQian int ret; 5803a69568SZhengShunQian 59*c37ff3fbSCaesar Wang ret = clk_prepare_enable(efuse->clk); 6003a69568SZhengShunQian if (ret < 0) { 61*c37ff3fbSCaesar Wang dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 6203a69568SZhengShunQian return ret; 6303a69568SZhengShunQian } 6403a69568SZhengShunQian 65*c37ff3fbSCaesar Wang writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL); 6603a69568SZhengShunQian udelay(1); 6703a69568SZhengShunQian while (val_size) { 68*c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 6903a69568SZhengShunQian (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), 70*c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 71*c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 7203a69568SZhengShunQian ((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT), 73*c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 7403a69568SZhengShunQian udelay(1); 75*c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 76*c37ff3fbSCaesar Wang EFUSE_STROBE, efuse->base + REG_EFUSE_CTRL); 7703a69568SZhengShunQian udelay(1); 78*c37ff3fbSCaesar Wang *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 79*c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 80*c37ff3fbSCaesar Wang (~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL); 8103a69568SZhengShunQian udelay(1); 8203a69568SZhengShunQian 8303a69568SZhengShunQian val_size -= 1; 8403a69568SZhengShunQian offset += 1; 8503a69568SZhengShunQian } 8603a69568SZhengShunQian 8703a69568SZhengShunQian /* Switch to standby mode */ 88*c37ff3fbSCaesar Wang writel(EFUSE_PGENB | EFUSE_CSB, efuse->base + REG_EFUSE_CTRL); 8903a69568SZhengShunQian 90*c37ff3fbSCaesar Wang clk_disable_unprepare(efuse->clk); 9103a69568SZhengShunQian 9203a69568SZhengShunQian return 0; 9303a69568SZhengShunQian } 9403a69568SZhengShunQian 9503a69568SZhengShunQian static struct regmap_bus rockchip_efuse_bus = { 9603a69568SZhengShunQian .read = rockchip_efuse_read, 9703a69568SZhengShunQian .write = rockchip_efuse_write, 9803a69568SZhengShunQian .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 9903a69568SZhengShunQian .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 10003a69568SZhengShunQian }; 10103a69568SZhengShunQian 1027e532f79Skbuild test robot static struct regmap_config rockchip_efuse_regmap_config = { 10303a69568SZhengShunQian .reg_bits = 32, 10403a69568SZhengShunQian .reg_stride = 1, 10503a69568SZhengShunQian .val_bits = 8, 10603a69568SZhengShunQian }; 10703a69568SZhengShunQian 10803a69568SZhengShunQian static struct nvmem_config econfig = { 10903a69568SZhengShunQian .name = "rockchip-efuse", 11003a69568SZhengShunQian .owner = THIS_MODULE, 11103a69568SZhengShunQian .read_only = true, 11203a69568SZhengShunQian }; 11303a69568SZhengShunQian 11403a69568SZhengShunQian static const struct of_device_id rockchip_efuse_match[] = { 11503a69568SZhengShunQian { .compatible = "rockchip,rockchip-efuse", }, 11603a69568SZhengShunQian { /* sentinel */}, 11703a69568SZhengShunQian }; 11803a69568SZhengShunQian MODULE_DEVICE_TABLE(of, rockchip_efuse_match); 11903a69568SZhengShunQian 1207e532f79Skbuild test robot static int rockchip_efuse_probe(struct platform_device *pdev) 12103a69568SZhengShunQian { 12203a69568SZhengShunQian struct resource *res; 12303a69568SZhengShunQian struct nvmem_device *nvmem; 12403a69568SZhengShunQian struct regmap *regmap; 125*c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse; 126*c37ff3fbSCaesar Wang 127*c37ff3fbSCaesar Wang efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), 128*c37ff3fbSCaesar Wang GFP_KERNEL); 129*c37ff3fbSCaesar Wang if (!efuse) 130*c37ff3fbSCaesar Wang return -ENOMEM; 13103a69568SZhengShunQian 13203a69568SZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133*c37ff3fbSCaesar Wang efuse->base = devm_ioremap_resource(&pdev->dev, res); 134*c37ff3fbSCaesar Wang if (IS_ERR(efuse->base)) 135*c37ff3fbSCaesar Wang return PTR_ERR(efuse->base); 13603a69568SZhengShunQian 137*c37ff3fbSCaesar Wang efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); 138*c37ff3fbSCaesar Wang if (IS_ERR(efuse->clk)) 139*c37ff3fbSCaesar Wang return PTR_ERR(efuse->clk); 14003a69568SZhengShunQian 141*c37ff3fbSCaesar Wang efuse->dev = &pdev->dev; 14203a69568SZhengShunQian 14303a69568SZhengShunQian rockchip_efuse_regmap_config.max_register = resource_size(res) - 1; 14403a69568SZhengShunQian 145*c37ff3fbSCaesar Wang regmap = devm_regmap_init(efuse->dev, &rockchip_efuse_bus, 146*c37ff3fbSCaesar Wang efuse, &rockchip_efuse_regmap_config); 14703a69568SZhengShunQian if (IS_ERR(regmap)) { 148*c37ff3fbSCaesar Wang dev_err(efuse->dev, "regmap init failed\n"); 14903a69568SZhengShunQian return PTR_ERR(regmap); 15003a69568SZhengShunQian } 151*c37ff3fbSCaesar Wang 152*c37ff3fbSCaesar Wang econfig.dev = efuse->dev; 15303a69568SZhengShunQian nvmem = nvmem_register(&econfig); 15403a69568SZhengShunQian if (IS_ERR(nvmem)) 15503a69568SZhengShunQian return PTR_ERR(nvmem); 15603a69568SZhengShunQian 15703a69568SZhengShunQian platform_set_drvdata(pdev, nvmem); 15803a69568SZhengShunQian 15903a69568SZhengShunQian return 0; 16003a69568SZhengShunQian } 16103a69568SZhengShunQian 1627e532f79Skbuild test robot static int rockchip_efuse_remove(struct platform_device *pdev) 16303a69568SZhengShunQian { 16403a69568SZhengShunQian struct nvmem_device *nvmem = platform_get_drvdata(pdev); 16503a69568SZhengShunQian 16603a69568SZhengShunQian return nvmem_unregister(nvmem); 16703a69568SZhengShunQian } 16803a69568SZhengShunQian 16903a69568SZhengShunQian static struct platform_driver rockchip_efuse_driver = { 17003a69568SZhengShunQian .probe = rockchip_efuse_probe, 17103a69568SZhengShunQian .remove = rockchip_efuse_remove, 17203a69568SZhengShunQian .driver = { 17303a69568SZhengShunQian .name = "rockchip-efuse", 17403a69568SZhengShunQian .of_match_table = rockchip_efuse_match, 17503a69568SZhengShunQian }, 17603a69568SZhengShunQian }; 17703a69568SZhengShunQian 17803a69568SZhengShunQian module_platform_driver(rockchip_efuse_driver); 17903a69568SZhengShunQian MODULE_DESCRIPTION("rockchip_efuse driver"); 18003a69568SZhengShunQian MODULE_LICENSE("GPL v2"); 181