/* * Rockchip eFuse Driver * * Copyright (c) 2015 Rockchip Electronics Co. Ltd. * Author: Caesar Wang * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include #include #include #include #include #include #include #include #include #include #define EFUSE_A_SHIFT 6 #define EFUSE_A_MASK 0x3ff #define EFUSE_PGENB BIT(3) #define EFUSE_LOAD BIT(2) #define EFUSE_STROBE BIT(1) #define EFUSE_CSB BIT(0) #define REG_EFUSE_CTRL 0x0000 #define REG_EFUSE_DOUT 0x0004 struct rockchip_efuse_context { struct device *dev; void __iomem *base; struct clk *efuse_clk; }; static int rockchip_efuse_write(void *context, const void *data, size_t count) { /* Nothing TBD, Read-Only */ return 0; } static int rockchip_efuse_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { unsigned int offset = *(u32 *)reg; struct rockchip_efuse_context *_context = context; void __iomem *base = _context->base; struct clk *clk = _context->efuse_clk; u8 *buf = val; int ret; ret = clk_prepare_enable(clk); if (ret < 0) { dev_err(_context->dev, "failed to prepare/enable efuse clk\n"); return ret; } writel(EFUSE_LOAD | EFUSE_PGENB, base + REG_EFUSE_CTRL); udelay(1); while (val_size) { writel(readl(base + REG_EFUSE_CTRL) & (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), base + REG_EFUSE_CTRL); writel(readl(base + REG_EFUSE_CTRL) | ((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT), base + REG_EFUSE_CTRL); udelay(1); writel(readl(base + REG_EFUSE_CTRL) | EFUSE_STROBE, base + REG_EFUSE_CTRL); udelay(1); *buf++ = readb(base + REG_EFUSE_DOUT); writel(readl(base + REG_EFUSE_CTRL) & (~EFUSE_STROBE), base + REG_EFUSE_CTRL); udelay(1); val_size -= 1; offset += 1; } /* Switch to standby mode */ writel(EFUSE_PGENB | EFUSE_CSB, base + REG_EFUSE_CTRL); clk_disable_unprepare(clk); return 0; } static struct regmap_bus rockchip_efuse_bus = { .read = rockchip_efuse_read, .write = rockchip_efuse_write, .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; static struct regmap_config rockchip_efuse_regmap_config = { .reg_bits = 32, .reg_stride = 1, .val_bits = 8, }; static struct nvmem_config econfig = { .name = "rockchip-efuse", .owner = THIS_MODULE, .read_only = true, }; static const struct of_device_id rockchip_efuse_match[] = { { .compatible = "rockchip,rockchip-efuse",}, { /* sentinel */}, }; MODULE_DEVICE_TABLE(of, rockchip_efuse_match); static int rockchip_efuse_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; struct regmap *regmap; void __iomem *base; struct clk *clk; struct rockchip_efuse_context *context; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) return PTR_ERR(base); context = devm_kzalloc(dev, sizeof(struct rockchip_efuse_context), GFP_KERNEL); if (IS_ERR(context)) return PTR_ERR(context); clk = devm_clk_get(dev, "pclk_efuse"); if (IS_ERR(clk)) return PTR_ERR(clk); context->dev = dev; context->base = base; context->efuse_clk = clk; rockchip_efuse_regmap_config.max_register = resource_size(res) - 1; regmap = devm_regmap_init(dev, &rockchip_efuse_bus, context, &rockchip_efuse_regmap_config); if (IS_ERR(regmap)) { dev_err(dev, "regmap init failed\n"); return PTR_ERR(regmap); } econfig.dev = dev; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); platform_set_drvdata(pdev, nvmem); return 0; } static int rockchip_efuse_remove(struct platform_device *pdev) { struct nvmem_device *nvmem = platform_get_drvdata(pdev); return nvmem_unregister(nvmem); } static struct platform_driver rockchip_efuse_driver = { .probe = rockchip_efuse_probe, .remove = rockchip_efuse_remove, .driver = { .name = "rockchip-efuse", .of_match_table = rockchip_efuse_match, }, }; module_platform_driver(rockchip_efuse_driver); MODULE_DESCRIPTION("rockchip_efuse driver"); MODULE_LICENSE("GPL v2");