1*03a69568SZhengShunQian /* 2*03a69568SZhengShunQian * Rockchip eFuse Driver 3*03a69568SZhengShunQian * 4*03a69568SZhengShunQian * Copyright (c) 2015 Rockchip Electronics Co. Ltd. 5*03a69568SZhengShunQian * Author: Caesar Wang <wxt@rock-chips.com> 6*03a69568SZhengShunQian * 7*03a69568SZhengShunQian * This program is free software; you can redistribute it and/or modify it 8*03a69568SZhengShunQian * under the terms of version 2 of the GNU General Public License as 9*03a69568SZhengShunQian * published by the Free Software Foundation. 10*03a69568SZhengShunQian * 11*03a69568SZhengShunQian * This program is distributed in the hope that it will be useful, but WITHOUT 12*03a69568SZhengShunQian * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*03a69568SZhengShunQian * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14*03a69568SZhengShunQian * more details. 15*03a69568SZhengShunQian */ 16*03a69568SZhengShunQian 17*03a69568SZhengShunQian #include <linux/platform_device.h> 18*03a69568SZhengShunQian #include <linux/nvmem-provider.h> 19*03a69568SZhengShunQian #include <linux/slab.h> 20*03a69568SZhengShunQian #include <linux/regmap.h> 21*03a69568SZhengShunQian #include <linux/device.h> 22*03a69568SZhengShunQian #include <linux/io.h> 23*03a69568SZhengShunQian #include <linux/module.h> 24*03a69568SZhengShunQian #include <linux/delay.h> 25*03a69568SZhengShunQian #include <linux/of.h> 26*03a69568SZhengShunQian #include <linux/clk.h> 27*03a69568SZhengShunQian 28*03a69568SZhengShunQian #define EFUSE_A_SHIFT 6 29*03a69568SZhengShunQian #define EFUSE_A_MASK 0x3ff 30*03a69568SZhengShunQian #define EFUSE_PGENB BIT(3) 31*03a69568SZhengShunQian #define EFUSE_LOAD BIT(2) 32*03a69568SZhengShunQian #define EFUSE_STROBE BIT(1) 33*03a69568SZhengShunQian #define EFUSE_CSB BIT(0) 34*03a69568SZhengShunQian 35*03a69568SZhengShunQian #define REG_EFUSE_CTRL 0x0000 36*03a69568SZhengShunQian #define REG_EFUSE_DOUT 0x0004 37*03a69568SZhengShunQian 38*03a69568SZhengShunQian struct rockchip_efuse_context { 39*03a69568SZhengShunQian struct device *dev; 40*03a69568SZhengShunQian void __iomem *base; 41*03a69568SZhengShunQian struct clk *efuse_clk; 42*03a69568SZhengShunQian }; 43*03a69568SZhengShunQian 44*03a69568SZhengShunQian static int rockchip_efuse_write(void *context, const void *data, size_t count) 45*03a69568SZhengShunQian { 46*03a69568SZhengShunQian /* Nothing TBD, Read-Only */ 47*03a69568SZhengShunQian return 0; 48*03a69568SZhengShunQian } 49*03a69568SZhengShunQian 50*03a69568SZhengShunQian static int rockchip_efuse_read(void *context, 51*03a69568SZhengShunQian const void *reg, size_t reg_size, 52*03a69568SZhengShunQian void *val, size_t val_size) 53*03a69568SZhengShunQian { 54*03a69568SZhengShunQian unsigned int offset = *(u32 *)reg; 55*03a69568SZhengShunQian struct rockchip_efuse_context *_context = context; 56*03a69568SZhengShunQian void __iomem *base = _context->base; 57*03a69568SZhengShunQian struct clk *clk = _context->efuse_clk; 58*03a69568SZhengShunQian u8 *buf = val; 59*03a69568SZhengShunQian int ret; 60*03a69568SZhengShunQian 61*03a69568SZhengShunQian ret = clk_prepare_enable(clk); 62*03a69568SZhengShunQian if (ret < 0) { 63*03a69568SZhengShunQian dev_err(_context->dev, "failed to prepare/enable efuse clk\n"); 64*03a69568SZhengShunQian return ret; 65*03a69568SZhengShunQian } 66*03a69568SZhengShunQian 67*03a69568SZhengShunQian writel(EFUSE_LOAD | EFUSE_PGENB, base + REG_EFUSE_CTRL); 68*03a69568SZhengShunQian udelay(1); 69*03a69568SZhengShunQian while (val_size) { 70*03a69568SZhengShunQian writel(readl(base + REG_EFUSE_CTRL) & 71*03a69568SZhengShunQian (~(EFUSE_A_MASK << EFUSE_A_SHIFT)), 72*03a69568SZhengShunQian base + REG_EFUSE_CTRL); 73*03a69568SZhengShunQian writel(readl(base + REG_EFUSE_CTRL) | 74*03a69568SZhengShunQian ((offset & EFUSE_A_MASK) << EFUSE_A_SHIFT), 75*03a69568SZhengShunQian base + REG_EFUSE_CTRL); 76*03a69568SZhengShunQian udelay(1); 77*03a69568SZhengShunQian writel(readl(base + REG_EFUSE_CTRL) | 78*03a69568SZhengShunQian EFUSE_STROBE, base + REG_EFUSE_CTRL); 79*03a69568SZhengShunQian udelay(1); 80*03a69568SZhengShunQian *buf++ = readb(base + REG_EFUSE_DOUT); 81*03a69568SZhengShunQian writel(readl(base + REG_EFUSE_CTRL) & 82*03a69568SZhengShunQian (~EFUSE_STROBE), base + REG_EFUSE_CTRL); 83*03a69568SZhengShunQian udelay(1); 84*03a69568SZhengShunQian 85*03a69568SZhengShunQian val_size -= 1; 86*03a69568SZhengShunQian offset += 1; 87*03a69568SZhengShunQian } 88*03a69568SZhengShunQian 89*03a69568SZhengShunQian /* Switch to standby mode */ 90*03a69568SZhengShunQian writel(EFUSE_PGENB | EFUSE_CSB, base + REG_EFUSE_CTRL); 91*03a69568SZhengShunQian 92*03a69568SZhengShunQian clk_disable_unprepare(clk); 93*03a69568SZhengShunQian 94*03a69568SZhengShunQian return 0; 95*03a69568SZhengShunQian } 96*03a69568SZhengShunQian 97*03a69568SZhengShunQian static struct regmap_bus rockchip_efuse_bus = { 98*03a69568SZhengShunQian .read = rockchip_efuse_read, 99*03a69568SZhengShunQian .write = rockchip_efuse_write, 100*03a69568SZhengShunQian .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 101*03a69568SZhengShunQian .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 102*03a69568SZhengShunQian }; 103*03a69568SZhengShunQian 104*03a69568SZhengShunQian struct regmap_config rockchip_efuse_regmap_config = { 105*03a69568SZhengShunQian .reg_bits = 32, 106*03a69568SZhengShunQian .reg_stride = 1, 107*03a69568SZhengShunQian .val_bits = 8, 108*03a69568SZhengShunQian }; 109*03a69568SZhengShunQian 110*03a69568SZhengShunQian static struct nvmem_config econfig = { 111*03a69568SZhengShunQian .name = "rockchip-efuse", 112*03a69568SZhengShunQian .owner = THIS_MODULE, 113*03a69568SZhengShunQian .read_only = true, 114*03a69568SZhengShunQian }; 115*03a69568SZhengShunQian 116*03a69568SZhengShunQian static const struct of_device_id rockchip_efuse_match[] = { 117*03a69568SZhengShunQian { .compatible = "rockchip,rockchip-efuse",}, 118*03a69568SZhengShunQian { /* sentinel */}, 119*03a69568SZhengShunQian }; 120*03a69568SZhengShunQian MODULE_DEVICE_TABLE(of, rockchip_efuse_match); 121*03a69568SZhengShunQian 122*03a69568SZhengShunQian int rockchip_efuse_probe(struct platform_device *pdev) 123*03a69568SZhengShunQian { 124*03a69568SZhengShunQian struct device *dev = &pdev->dev; 125*03a69568SZhengShunQian struct resource *res; 126*03a69568SZhengShunQian struct nvmem_device *nvmem; 127*03a69568SZhengShunQian struct regmap *regmap; 128*03a69568SZhengShunQian void __iomem *base; 129*03a69568SZhengShunQian struct clk *clk; 130*03a69568SZhengShunQian struct rockchip_efuse_context *context; 131*03a69568SZhengShunQian 132*03a69568SZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133*03a69568SZhengShunQian base = devm_ioremap_resource(dev, res); 134*03a69568SZhengShunQian if (IS_ERR(base)) 135*03a69568SZhengShunQian return PTR_ERR(base); 136*03a69568SZhengShunQian 137*03a69568SZhengShunQian context = devm_kzalloc(dev, sizeof(struct rockchip_efuse_context), 138*03a69568SZhengShunQian GFP_KERNEL); 139*03a69568SZhengShunQian if (IS_ERR(context)) 140*03a69568SZhengShunQian return PTR_ERR(context); 141*03a69568SZhengShunQian 142*03a69568SZhengShunQian clk = devm_clk_get(dev, "pclk_efuse"); 143*03a69568SZhengShunQian if (IS_ERR(clk)) 144*03a69568SZhengShunQian return PTR_ERR(clk); 145*03a69568SZhengShunQian 146*03a69568SZhengShunQian context->dev = dev; 147*03a69568SZhengShunQian context->base = base; 148*03a69568SZhengShunQian context->efuse_clk = clk; 149*03a69568SZhengShunQian 150*03a69568SZhengShunQian rockchip_efuse_regmap_config.max_register = resource_size(res) - 1; 151*03a69568SZhengShunQian 152*03a69568SZhengShunQian regmap = devm_regmap_init(dev, &rockchip_efuse_bus, 153*03a69568SZhengShunQian context, &rockchip_efuse_regmap_config); 154*03a69568SZhengShunQian if (IS_ERR(regmap)) { 155*03a69568SZhengShunQian dev_err(dev, "regmap init failed\n"); 156*03a69568SZhengShunQian return PTR_ERR(regmap); 157*03a69568SZhengShunQian } 158*03a69568SZhengShunQian econfig.dev = dev; 159*03a69568SZhengShunQian nvmem = nvmem_register(&econfig); 160*03a69568SZhengShunQian if (IS_ERR(nvmem)) 161*03a69568SZhengShunQian return PTR_ERR(nvmem); 162*03a69568SZhengShunQian 163*03a69568SZhengShunQian platform_set_drvdata(pdev, nvmem); 164*03a69568SZhengShunQian 165*03a69568SZhengShunQian return 0; 166*03a69568SZhengShunQian } 167*03a69568SZhengShunQian 168*03a69568SZhengShunQian int rockchip_efuse_remove(struct platform_device *pdev) 169*03a69568SZhengShunQian { 170*03a69568SZhengShunQian struct nvmem_device *nvmem = platform_get_drvdata(pdev); 171*03a69568SZhengShunQian 172*03a69568SZhengShunQian return nvmem_unregister(nvmem); 173*03a69568SZhengShunQian } 174*03a69568SZhengShunQian 175*03a69568SZhengShunQian static struct platform_driver rockchip_efuse_driver = { 176*03a69568SZhengShunQian .probe = rockchip_efuse_probe, 177*03a69568SZhengShunQian .remove = rockchip_efuse_remove, 178*03a69568SZhengShunQian .driver = { 179*03a69568SZhengShunQian .name = "rockchip-efuse", 180*03a69568SZhengShunQian .of_match_table = rockchip_efuse_match, 181*03a69568SZhengShunQian }, 182*03a69568SZhengShunQian }; 183*03a69568SZhengShunQian 184*03a69568SZhengShunQian module_platform_driver(rockchip_efuse_driver); 185*03a69568SZhengShunQian MODULE_DESCRIPTION("rockchip_efuse driver"); 186*03a69568SZhengShunQian MODULE_LICENSE("GPL v2"); 187