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> 25*02baff32SFinley Xiao #include <linux/of_platform.h> 26c37ff3fbSCaesar Wang #include <linux/platform_device.h> 2703a69568SZhengShunQian 28*02baff32SFinley Xiao #define RK3288_A_SHIFT 6 29*02baff32SFinley Xiao #define RK3288_A_MASK 0x3ff 30*02baff32SFinley Xiao #define RK3288_PGENB BIT(3) 31*02baff32SFinley Xiao #define RK3288_LOAD BIT(2) 32*02baff32SFinley Xiao #define RK3288_STROBE BIT(1) 33*02baff32SFinley Xiao #define RK3288_CSB BIT(0) 34*02baff32SFinley Xiao 35*02baff32SFinley Xiao #define RK3399_A_SHIFT 16 36*02baff32SFinley Xiao #define RK3399_A_MASK 0x3ff 37*02baff32SFinley Xiao #define RK3399_NBYTES 4 38*02baff32SFinley Xiao #define RK3399_STROBSFTSEL BIT(9) 39*02baff32SFinley Xiao #define RK3399_RSB BIT(7) 40*02baff32SFinley Xiao #define RK3399_PD BIT(5) 41*02baff32SFinley Xiao #define RK3399_PGENB BIT(3) 42*02baff32SFinley Xiao #define RK3399_LOAD BIT(2) 43*02baff32SFinley Xiao #define RK3399_STROBE BIT(1) 44*02baff32SFinley Xiao #define RK3399_CSB BIT(0) 4503a69568SZhengShunQian 4603a69568SZhengShunQian #define REG_EFUSE_CTRL 0x0000 4703a69568SZhengShunQian #define REG_EFUSE_DOUT 0x0004 4803a69568SZhengShunQian 49c37ff3fbSCaesar Wang struct rockchip_efuse_chip { 5003a69568SZhengShunQian struct device *dev; 5103a69568SZhengShunQian void __iomem *base; 52c37ff3fbSCaesar Wang struct clk *clk; 5303a69568SZhengShunQian }; 5403a69568SZhengShunQian 55*02baff32SFinley Xiao static int rockchip_rk3288_efuse_read(void *context, unsigned int offset, 56cc907553SSrinivas Kandagatla void *val, size_t bytes) 5703a69568SZhengShunQian { 58c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse = context; 5903a69568SZhengShunQian u8 *buf = val; 6003a69568SZhengShunQian int ret; 6103a69568SZhengShunQian 62c37ff3fbSCaesar Wang ret = clk_prepare_enable(efuse->clk); 6303a69568SZhengShunQian if (ret < 0) { 64c37ff3fbSCaesar Wang dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 6503a69568SZhengShunQian return ret; 6603a69568SZhengShunQian } 6703a69568SZhengShunQian 68*02baff32SFinley Xiao writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL); 6903a69568SZhengShunQian udelay(1); 70cc907553SSrinivas Kandagatla while (bytes--) { 71c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 72*02baff32SFinley Xiao (~(RK3288_A_MASK << RK3288_A_SHIFT)), 73c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 74c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 75*02baff32SFinley Xiao ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT), 76c37ff3fbSCaesar Wang efuse->base + REG_EFUSE_CTRL); 7703a69568SZhengShunQian udelay(1); 78c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) | 79*02baff32SFinley Xiao RK3288_STROBE, efuse->base + REG_EFUSE_CTRL); 8003a69568SZhengShunQian udelay(1); 81c37ff3fbSCaesar Wang *buf++ = readb(efuse->base + REG_EFUSE_DOUT); 82c37ff3fbSCaesar Wang writel(readl(efuse->base + REG_EFUSE_CTRL) & 83*02baff32SFinley Xiao (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL); 8403a69568SZhengShunQian udelay(1); 8503a69568SZhengShunQian } 8603a69568SZhengShunQian 8703a69568SZhengShunQian /* Switch to standby mode */ 88*02baff32SFinley Xiao writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL); 89*02baff32SFinley Xiao 90*02baff32SFinley Xiao clk_disable_unprepare(efuse->clk); 91*02baff32SFinley Xiao 92*02baff32SFinley Xiao return 0; 93*02baff32SFinley Xiao } 94*02baff32SFinley Xiao 95*02baff32SFinley Xiao static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, 96*02baff32SFinley Xiao void *val, size_t bytes) 97*02baff32SFinley Xiao { 98*02baff32SFinley Xiao struct rockchip_efuse_chip *efuse = context; 99*02baff32SFinley Xiao unsigned int addr_start, addr_end, addr_offset, addr_len; 100*02baff32SFinley Xiao u32 out_value; 101*02baff32SFinley Xiao u8 *buf; 102*02baff32SFinley Xiao int ret, i = 0; 103*02baff32SFinley Xiao 104*02baff32SFinley Xiao ret = clk_prepare_enable(efuse->clk); 105*02baff32SFinley Xiao if (ret < 0) { 106*02baff32SFinley Xiao dev_err(efuse->dev, "failed to prepare/enable efuse clk\n"); 107*02baff32SFinley Xiao return ret; 108*02baff32SFinley Xiao } 109*02baff32SFinley Xiao 110*02baff32SFinley Xiao addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES; 111*02baff32SFinley Xiao addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES; 112*02baff32SFinley Xiao addr_offset = offset % RK3399_NBYTES; 113*02baff32SFinley Xiao addr_len = addr_end - addr_start; 114*02baff32SFinley Xiao 115*02baff32SFinley Xiao buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); 116*02baff32SFinley Xiao if (!buf) { 117*02baff32SFinley Xiao clk_disable_unprepare(efuse->clk); 118*02baff32SFinley Xiao return -ENOMEM; 119*02baff32SFinley Xiao } 120*02baff32SFinley Xiao 121*02baff32SFinley Xiao writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB, 122*02baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 123*02baff32SFinley Xiao udelay(1); 124*02baff32SFinley Xiao while (addr_len--) { 125*02baff32SFinley Xiao writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE | 126*02baff32SFinley Xiao ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT), 127*02baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 128*02baff32SFinley Xiao udelay(1); 129*02baff32SFinley Xiao out_value = readl(efuse->base + REG_EFUSE_DOUT); 130*02baff32SFinley Xiao writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE), 131*02baff32SFinley Xiao efuse->base + REG_EFUSE_CTRL); 132*02baff32SFinley Xiao udelay(1); 133*02baff32SFinley Xiao 134*02baff32SFinley Xiao memcpy(&buf[i], &out_value, RK3399_NBYTES); 135*02baff32SFinley Xiao i += RK3399_NBYTES; 136*02baff32SFinley Xiao } 137*02baff32SFinley Xiao 138*02baff32SFinley Xiao /* Switch to standby mode */ 139*02baff32SFinley Xiao writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL); 140*02baff32SFinley Xiao 141*02baff32SFinley Xiao memcpy(val, buf + addr_offset, bytes); 142*02baff32SFinley Xiao 143*02baff32SFinley Xiao kfree(buf); 14403a69568SZhengShunQian 145c37ff3fbSCaesar Wang clk_disable_unprepare(efuse->clk); 14603a69568SZhengShunQian 14703a69568SZhengShunQian return 0; 14803a69568SZhengShunQian } 14903a69568SZhengShunQian 15003a69568SZhengShunQian static struct nvmem_config econfig = { 15103a69568SZhengShunQian .name = "rockchip-efuse", 15203a69568SZhengShunQian .owner = THIS_MODULE, 153cc907553SSrinivas Kandagatla .stride = 1, 154cc907553SSrinivas Kandagatla .word_size = 1, 15503a69568SZhengShunQian .read_only = true, 15603a69568SZhengShunQian }; 15703a69568SZhengShunQian 15803a69568SZhengShunQian static const struct of_device_id rockchip_efuse_match[] = { 159*02baff32SFinley Xiao /* deprecated but kept around for dts binding compatibility */ 160*02baff32SFinley Xiao { 161*02baff32SFinley Xiao .compatible = "rockchip,rockchip-efuse", 162*02baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 163*02baff32SFinley Xiao }, 164*02baff32SFinley Xiao { 165*02baff32SFinley Xiao .compatible = "rockchip,rk3066a-efuse", 166*02baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 167*02baff32SFinley Xiao }, 168*02baff32SFinley Xiao { 169*02baff32SFinley Xiao .compatible = "rockchip,rk3188-efuse", 170*02baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 171*02baff32SFinley Xiao }, 172*02baff32SFinley Xiao { 173*02baff32SFinley Xiao .compatible = "rockchip,rk3288-efuse", 174*02baff32SFinley Xiao .data = (void *)&rockchip_rk3288_efuse_read, 175*02baff32SFinley Xiao }, 176*02baff32SFinley Xiao { 177*02baff32SFinley Xiao .compatible = "rockchip,rk3399-efuse", 178*02baff32SFinley Xiao .data = (void *)&rockchip_rk3399_efuse_read, 179*02baff32SFinley Xiao }, 18003a69568SZhengShunQian { /* sentinel */}, 18103a69568SZhengShunQian }; 18203a69568SZhengShunQian MODULE_DEVICE_TABLE(of, rockchip_efuse_match); 18303a69568SZhengShunQian 1847e532f79Skbuild test robot static int rockchip_efuse_probe(struct platform_device *pdev) 18503a69568SZhengShunQian { 18603a69568SZhengShunQian struct resource *res; 18703a69568SZhengShunQian struct nvmem_device *nvmem; 188c37ff3fbSCaesar Wang struct rockchip_efuse_chip *efuse; 189*02baff32SFinley Xiao const struct of_device_id *match; 190*02baff32SFinley Xiao struct device *dev = &pdev->dev; 191*02baff32SFinley Xiao 192*02baff32SFinley Xiao match = of_match_device(dev->driver->of_match_table, dev); 193*02baff32SFinley Xiao if (!match || !match->data) { 194*02baff32SFinley Xiao dev_err(dev, "failed to get match data\n"); 195*02baff32SFinley Xiao return -EINVAL; 196*02baff32SFinley Xiao } 197c37ff3fbSCaesar Wang 198c37ff3fbSCaesar Wang efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), 199c37ff3fbSCaesar Wang GFP_KERNEL); 200c37ff3fbSCaesar Wang if (!efuse) 201c37ff3fbSCaesar Wang return -ENOMEM; 20203a69568SZhengShunQian 20303a69568SZhengShunQian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 204c37ff3fbSCaesar Wang efuse->base = devm_ioremap_resource(&pdev->dev, res); 205c37ff3fbSCaesar Wang if (IS_ERR(efuse->base)) 206c37ff3fbSCaesar Wang return PTR_ERR(efuse->base); 20703a69568SZhengShunQian 208c37ff3fbSCaesar Wang efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); 209c37ff3fbSCaesar Wang if (IS_ERR(efuse->clk)) 210c37ff3fbSCaesar Wang return PTR_ERR(efuse->clk); 21103a69568SZhengShunQian 212c37ff3fbSCaesar Wang efuse->dev = &pdev->dev; 213cc907553SSrinivas Kandagatla econfig.size = resource_size(res); 214*02baff32SFinley Xiao econfig.reg_read = match->data; 215cc907553SSrinivas Kandagatla econfig.priv = efuse; 216c37ff3fbSCaesar Wang econfig.dev = efuse->dev; 21703a69568SZhengShunQian nvmem = nvmem_register(&econfig); 21803a69568SZhengShunQian if (IS_ERR(nvmem)) 21903a69568SZhengShunQian return PTR_ERR(nvmem); 22003a69568SZhengShunQian 22103a69568SZhengShunQian platform_set_drvdata(pdev, nvmem); 22203a69568SZhengShunQian 22303a69568SZhengShunQian return 0; 22403a69568SZhengShunQian } 22503a69568SZhengShunQian 2267e532f79Skbuild test robot static int rockchip_efuse_remove(struct platform_device *pdev) 22703a69568SZhengShunQian { 22803a69568SZhengShunQian struct nvmem_device *nvmem = platform_get_drvdata(pdev); 22903a69568SZhengShunQian 23003a69568SZhengShunQian return nvmem_unregister(nvmem); 23103a69568SZhengShunQian } 23203a69568SZhengShunQian 23303a69568SZhengShunQian static struct platform_driver rockchip_efuse_driver = { 23403a69568SZhengShunQian .probe = rockchip_efuse_probe, 23503a69568SZhengShunQian .remove = rockchip_efuse_remove, 23603a69568SZhengShunQian .driver = { 23703a69568SZhengShunQian .name = "rockchip-efuse", 23803a69568SZhengShunQian .of_match_table = rockchip_efuse_match, 23903a69568SZhengShunQian }, 24003a69568SZhengShunQian }; 24103a69568SZhengShunQian 24203a69568SZhengShunQian module_platform_driver(rockchip_efuse_driver); 24303a69568SZhengShunQian MODULE_DESCRIPTION("rockchip_efuse driver"); 24403a69568SZhengShunQian MODULE_LICENSE("GPL v2"); 245