brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 013e671 Raw
303 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip eFuse Driver4 *5 * Copyright (c) 2015 Rockchip Electronics Co. Ltd.6 * Author: Caesar Wang <wxt@rock-chips.com>7 */8 9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/nvmem-provider.h>15#include <linux/slab.h>16#include <linux/of.h>17#include <linux/of_platform.h>18#include <linux/platform_device.h>19 20#define RK3288_A_SHIFT		621#define RK3288_A_MASK		0x3ff22#define RK3288_PGENB		BIT(3)23#define RK3288_LOAD		BIT(2)24#define RK3288_STROBE		BIT(1)25#define RK3288_CSB		BIT(0)26 27#define RK3328_SECURE_SIZES	9628#define RK3328_INT_STATUS	0x001829#define RK3328_DOUT		0x002030#define RK3328_AUTO_CTRL	0x002431#define RK3328_INT_FINISH	BIT(0)32#define RK3328_AUTO_ENB		BIT(0)33#define RK3328_AUTO_RD		BIT(1)34 35#define RK3399_A_SHIFT		1636#define RK3399_A_MASK		0x3ff37#define RK3399_NBYTES		438#define RK3399_STROBSFTSEL	BIT(9)39#define RK3399_RSB		BIT(7)40#define RK3399_PD		BIT(5)41#define RK3399_PGENB		BIT(3)42#define RK3399_LOAD		BIT(2)43#define RK3399_STROBE		BIT(1)44#define RK3399_CSB		BIT(0)45 46#define REG_EFUSE_CTRL		0x000047#define REG_EFUSE_DOUT		0x000448 49struct rockchip_efuse_chip {50	struct device *dev;51	void __iomem *base;52	struct clk *clk;53};54 55static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,56				      void *val, size_t bytes)57{58	struct rockchip_efuse_chip *efuse = context;59	u8 *buf = val;60	int ret;61 62	ret = clk_prepare_enable(efuse->clk);63	if (ret < 0) {64		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");65		return ret;66	}67 68	writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL);69	udelay(1);70	while (bytes--) {71		writel(readl(efuse->base + REG_EFUSE_CTRL) &72			     (~(RK3288_A_MASK << RK3288_A_SHIFT)),73			     efuse->base + REG_EFUSE_CTRL);74		writel(readl(efuse->base + REG_EFUSE_CTRL) |75			     ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),76			     efuse->base + REG_EFUSE_CTRL);77		udelay(1);78		writel(readl(efuse->base + REG_EFUSE_CTRL) |79			     RK3288_STROBE, efuse->base + REG_EFUSE_CTRL);80		udelay(1);81		*buf++ = readb(efuse->base + REG_EFUSE_DOUT);82		writel(readl(efuse->base + REG_EFUSE_CTRL) &83		       (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL);84		udelay(1);85	}86 87	/* Switch to standby mode */88	writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL);89 90	clk_disable_unprepare(efuse->clk);91 92	return 0;93}94 95static int rockchip_rk3328_efuse_read(void *context, unsigned int offset,96				      void *val, size_t bytes)97{98	struct rockchip_efuse_chip *efuse = context;99	unsigned int addr_start, addr_end, addr_offset, addr_len;100	u32 out_value, status;101	u8 *buf;102	int ret, i = 0;103 104	ret = clk_prepare_enable(efuse->clk);105	if (ret < 0) {106		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");107		return ret;108	}109 110	/* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */111	offset += RK3328_SECURE_SIZES;112	addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES;113	addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES;114	addr_offset = offset % RK3399_NBYTES;115	addr_len = addr_end - addr_start;116 117	buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)),118		      GFP_KERNEL);119	if (!buf) {120		ret = -ENOMEM;121		goto nomem;122	}123 124	while (addr_len--) {125		writel(RK3328_AUTO_RD | RK3328_AUTO_ENB |126		       ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),127		       efuse->base + RK3328_AUTO_CTRL);128		udelay(4);129		status = readl(efuse->base + RK3328_INT_STATUS);130		if (!(status & RK3328_INT_FINISH)) {131			ret = -EIO;132			goto err;133		}134		out_value = readl(efuse->base + RK3328_DOUT);135		writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS);136 137		memcpy(&buf[i], &out_value, RK3399_NBYTES);138		i += RK3399_NBYTES;139	}140 141	memcpy(val, buf + addr_offset, bytes);142err:143	kfree(buf);144nomem:145	clk_disable_unprepare(efuse->clk);146 147	return ret;148}149 150static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,151				      void *val, size_t bytes)152{153	struct rockchip_efuse_chip *efuse = context;154	unsigned int addr_start, addr_end, addr_offset, addr_len;155	u32 out_value;156	u8 *buf;157	int ret, i = 0;158 159	ret = clk_prepare_enable(efuse->clk);160	if (ret < 0) {161		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");162		return ret;163	}164 165	addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES;166	addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES;167	addr_offset = offset % RK3399_NBYTES;168	addr_len = addr_end - addr_start;169 170	buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)),171		      GFP_KERNEL);172	if (!buf) {173		clk_disable_unprepare(efuse->clk);174		return -ENOMEM;175	}176 177	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,178	       efuse->base + REG_EFUSE_CTRL);179	udelay(1);180	while (addr_len--) {181		writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE |182		       ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),183		       efuse->base + REG_EFUSE_CTRL);184		udelay(1);185		out_value = readl(efuse->base + REG_EFUSE_DOUT);186		writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE),187		       efuse->base + REG_EFUSE_CTRL);188		udelay(1);189 190		memcpy(&buf[i], &out_value, RK3399_NBYTES);191		i += RK3399_NBYTES;192	}193 194	/* Switch to standby mode */195	writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL);196 197	memcpy(val, buf + addr_offset, bytes);198 199	kfree(buf);200 201	clk_disable_unprepare(efuse->clk);202 203	return 0;204}205 206static struct nvmem_config econfig = {207	.name = "rockchip-efuse",208	.add_legacy_fixed_of_cells = true,209	.type = NVMEM_TYPE_OTP,210	.stride = 1,211	.word_size = 1,212	.read_only = true,213};214 215static const struct of_device_id rockchip_efuse_match[] = {216	/* deprecated but kept around for dts binding compatibility */217	{218		.compatible = "rockchip,rockchip-efuse",219		.data = (void *)&rockchip_rk3288_efuse_read,220	},221	{222		.compatible = "rockchip,rk3066a-efuse",223		.data = (void *)&rockchip_rk3288_efuse_read,224	},225	{226		.compatible = "rockchip,rk3188-efuse",227		.data = (void *)&rockchip_rk3288_efuse_read,228	},229	{230		.compatible = "rockchip,rk3228-efuse",231		.data = (void *)&rockchip_rk3288_efuse_read,232	},233	{234		.compatible = "rockchip,rk3288-efuse",235		.data = (void *)&rockchip_rk3288_efuse_read,236	},237	{238		.compatible = "rockchip,rk3368-efuse",239		.data = (void *)&rockchip_rk3288_efuse_read,240	},241	{242		.compatible = "rockchip,rk3328-efuse",243		.data = (void *)&rockchip_rk3328_efuse_read,244	},245	{246		.compatible = "rockchip,rk3399-efuse",247		.data = (void *)&rockchip_rk3399_efuse_read,248	},249	{ /* sentinel */},250};251MODULE_DEVICE_TABLE(of, rockchip_efuse_match);252 253static int rockchip_efuse_probe(struct platform_device *pdev)254{255	struct resource *res;256	struct nvmem_device *nvmem;257	struct rockchip_efuse_chip *efuse;258	const void *data;259	struct device *dev = &pdev->dev;260 261	data = of_device_get_match_data(dev);262	if (!data) {263		dev_err(dev, "failed to get match data\n");264		return -EINVAL;265	}266 267	efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),268			     GFP_KERNEL);269	if (!efuse)270		return -ENOMEM;271 272	efuse->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);273	if (IS_ERR(efuse->base))274		return PTR_ERR(efuse->base);275 276	efuse->clk = devm_clk_get(dev, "pclk_efuse");277	if (IS_ERR(efuse->clk))278		return PTR_ERR(efuse->clk);279 280	efuse->dev = dev;281	if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",282				 &econfig.size))283		econfig.size = resource_size(res);284	econfig.reg_read = data;285	econfig.priv = efuse;286	econfig.dev = efuse->dev;287	nvmem = devm_nvmem_register(dev, &econfig);288 289	return PTR_ERR_OR_ZERO(nvmem);290}291 292static struct platform_driver rockchip_efuse_driver = {293	.probe = rockchip_efuse_probe,294	.driver = {295		.name = "rockchip-efuse",296		.of_match_table = rockchip_efuse_match,297	},298};299 300module_platform_driver(rockchip_efuse_driver);301MODULE_DESCRIPTION("rockchip_efuse driver");302MODULE_LICENSE("GPL v2");303