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> 25c37ff3fbSCaesar Wang #include <linux/platform_device.h> 2603a69568SZhengShunQian 2703a69568SZhengShunQian #define EFUSE_A_SHIFT 6 2803a69568SZhengShunQian #define EFUSE_A_MASK 0x3ff 2903a69568SZhengShunQian #define EFUSE_PGENB BIT(3) 3003a69568SZhengShunQian #define EFUSE_LOAD BIT(2) 3103a69568SZhengShunQian #define EFUSE_STROBE BIT(1) 3203a69568SZhengShunQian #define EFUSE_CSB BIT(0) 3303a69568SZhengShunQian 3403a69568SZhengShunQian #define REG_EFUSE_CTRL 0x0000 3503a69568SZhengShunQian #define REG_EFUSE_DOUT 0x0004 3603a69568SZhengShunQian 37c37ff3fbSCaesar Wang struct rockchip_efuse_chip { 3803a69568SZhengShunQian struct device *dev; 3903a69568SZhengShunQian void __iomem *base; 40c37ff3fbSCaesar Wang struct clk *clk; 4103a69568SZhengShunQian }; 4203a69568SZhengShunQian 43*cc907553SSrinivas Kandagatla static int rockchip_efuse_read(void *context, unsigned int offset, 44*cc907553SSrinivas Kandagatla void *val, size_t bytes) 4503a69568SZhengShunQian { 46c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse = context; 4703a69568SZhengShunQian u8 *buf = val; 4803a69568SZhengShunQian int ret; 4903a69568SZhengShunQian 50c37ff3fbSCaesar Wang ret = clk_prepare_enable(efuse->clk); 5103a69568SZhengShunQian if (ret < 0) { 52c37ff3fbSCaesar Wang dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 5303a69568SZhengShunQian return ret; 5403a69568SZhengShunQian } 5503a69568SZhengShunQian 56c37ff3fbSCaesar Wang writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL); 5703a69568SZhengShunQian udelay(1); 58*cc907553SSrinivas Kandagatla while (bytes--) { 59c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 6003a69568SZhengShunQian (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), 61c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 62c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 63*cc907553SSrinivas Kandagatla ((offset++ & EFUSE_A_MASK) << EFUSE_A_SHIFT), 64c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 6503a69568SZhengShunQian udelay(1); 66c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 67c37ff3fbSCaesar Wang EFUSE_STROBE, efuse->base + REG_EFUSE_CTRL); 6803a69568SZhengShunQian udelay(1); 69c37ff3fbSCaesar Wang *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 70c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 71c37ff3fbSCaesar Wang (~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL); 7203a69568SZhengShunQian udelay(1); 7303a69568SZhengShunQian } 7403a69568SZhengShunQian 7503a69568SZhengShunQian /* Switch to standby mode */ 76c37ff3fbSCaesar Wang writel(EFUSE_PGENB | EFUSE_CSB, efuse->base + REG_EFUSE_CTRL); 7703a69568SZhengShunQian 78c37ff3fbSCaesar Wang clk_disable_unprepare(efuse->clk); 7903a69568SZhengShunQian 8003a69568SZhengShunQian return 0; 8103a69568SZhengShunQian } 8203a69568SZhengShunQian 8303a69568SZhengShunQian static struct nvmem_config econfig = { 8403a69568SZhengShunQian .name = "rockchip-efuse", 8503a69568SZhengShunQian .owner = THIS_MODULE, 86*cc907553SSrinivas Kandagatla .stride = 1, 87*cc907553SSrinivas Kandagatla .word_size = 1, 8803a69568SZhengShunQian .read_only = true, 8903a69568SZhengShunQian }; 9003a69568SZhengShunQian 9103a69568SZhengShunQian static const struct of_device_id rockchip_efuse_match[] = { 9203a69568SZhengShunQian { .compatible = "rockchip,rockchip-efuse", }, 9303a69568SZhengShunQian { /* sentinel */}, 9403a69568SZhengShunQian }; 9503a69568SZhengShunQian MODULE_DEVICE_TABLE(of, rockchip_efuse_match); 9603a69568SZhengShunQian 977e532f79Skbuild test robot static int rockchip_efuse_probe(struct platform_device *pdev) 9803a69568SZhengShunQian { 9903a69568SZhengShunQian struct resource *res; 10003a69568SZhengShunQian struct nvmem_device *nvmem; 101c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse; 102c37ff3fbSCaesar Wang 103c37ff3fbSCaesar Wang efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), 104c37ff3fbSCaesar Wang GFP_KERNEL); 105c37ff3fbSCaesar Wang if (!efuse) 106c37ff3fbSCaesar Wang return -ENOMEM; 10703a69568SZhengShunQian 10803a69568SZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 109c37ff3fbSCaesar Wang efuse->base = devm_ioremap_resource(&pdev->dev, res); 110c37ff3fbSCaesar Wang if (IS_ERR(efuse->base)) 111c37ff3fbSCaesar Wang return PTR_ERR(efuse->base); 11203a69568SZhengShunQian 113c37ff3fbSCaesar Wang efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); 114c37ff3fbSCaesar Wang if (IS_ERR(efuse->clk)) 115c37ff3fbSCaesar Wang return PTR_ERR(efuse->clk); 11603a69568SZhengShunQian 117c37ff3fbSCaesar Wang efuse->dev = &pdev->dev; 118*cc907553SSrinivas Kandagatla econfig.size = resource_size(res); 119*cc907553SSrinivas Kandagatla econfig.reg_read = rockchip_efuse_read; 120*cc907553SSrinivas Kandagatla econfig.priv = efuse; 121c37ff3fbSCaesar Wang econfig.dev = efuse->dev; 12203a69568SZhengShunQian nvmem = nvmem_register(&econfig); 12303a69568SZhengShunQian if (IS_ERR(nvmem)) 12403a69568SZhengShunQian return PTR_ERR(nvmem); 12503a69568SZhengShunQian 12603a69568SZhengShunQian platform_set_drvdata(pdev, nvmem); 12703a69568SZhengShunQian 12803a69568SZhengShunQian return 0; 12903a69568SZhengShunQian } 13003a69568SZhengShunQian 1317e532f79Skbuild test robot static int rockchip_efuse_remove(struct platform_device *pdev) 13203a69568SZhengShunQian { 13303a69568SZhengShunQian struct nvmem_device *nvmem = platform_get_drvdata(pdev); 13403a69568SZhengShunQian 13503a69568SZhengShunQian return nvmem_unregister(nvmem); 13603a69568SZhengShunQian } 13703a69568SZhengShunQian 13803a69568SZhengShunQian static struct platform_driver rockchip_efuse_driver = { 13903a69568SZhengShunQian .probe = rockchip_efuse_probe, 14003a69568SZhengShunQian .remove = rockchip_efuse_remove, 14103a69568SZhengShunQian .driver = { 14203a69568SZhengShunQian .name = "rockchip-efuse", 14303a69568SZhengShunQian .of_match_table = rockchip_efuse_match, 14403a69568SZhengShunQian }, 14503a69568SZhengShunQian }; 14603a69568SZhengShunQian 14703a69568SZhengShunQian module_platform_driver(rockchip_efuse_driver); 14803a69568SZhengShunQian MODULE_DESCRIPTION("rockchip_efuse driver"); 14903a69568SZhengShunQian MODULE_LICENSE("GPL v2"); 150