cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rockchip-efuse.c (7764B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Rockchip eFuse Driver
      4 *
      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		6
     21#define RK3288_A_MASK		0x3ff
     22#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	96
     28#define RK3328_INT_STATUS	0x0018
     29#define RK3328_DOUT		0x0020
     30#define RK3328_AUTO_CTRL	0x0024
     31#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		16
     36#define RK3399_A_MASK		0x3ff
     37#define RK3399_NBYTES		4
     38#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		0x0000
     47#define REG_EFUSE_DOUT		0x0004
     48
     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	.stride = 1,
    209	.word_size = 1,
    210	.read_only = true,
    211};
    212
    213static const struct of_device_id rockchip_efuse_match[] = {
    214	/* deprecated but kept around for dts binding compatibility */
    215	{
    216		.compatible = "rockchip,rockchip-efuse",
    217		.data = (void *)&rockchip_rk3288_efuse_read,
    218	},
    219	{
    220		.compatible = "rockchip,rk3066a-efuse",
    221		.data = (void *)&rockchip_rk3288_efuse_read,
    222	},
    223	{
    224		.compatible = "rockchip,rk3188-efuse",
    225		.data = (void *)&rockchip_rk3288_efuse_read,
    226	},
    227	{
    228		.compatible = "rockchip,rk3228-efuse",
    229		.data = (void *)&rockchip_rk3288_efuse_read,
    230	},
    231	{
    232		.compatible = "rockchip,rk3288-efuse",
    233		.data = (void *)&rockchip_rk3288_efuse_read,
    234	},
    235	{
    236		.compatible = "rockchip,rk3368-efuse",
    237		.data = (void *)&rockchip_rk3288_efuse_read,
    238	},
    239	{
    240		.compatible = "rockchip,rk3328-efuse",
    241		.data = (void *)&rockchip_rk3328_efuse_read,
    242	},
    243	{
    244		.compatible = "rockchip,rk3399-efuse",
    245		.data = (void *)&rockchip_rk3399_efuse_read,
    246	},
    247	{ /* sentinel */},
    248};
    249MODULE_DEVICE_TABLE(of, rockchip_efuse_match);
    250
    251static int rockchip_efuse_probe(struct platform_device *pdev)
    252{
    253	struct resource *res;
    254	struct nvmem_device *nvmem;
    255	struct rockchip_efuse_chip *efuse;
    256	const void *data;
    257	struct device *dev = &pdev->dev;
    258
    259	data = of_device_get_match_data(dev);
    260	if (!data) {
    261		dev_err(dev, "failed to get match data\n");
    262		return -EINVAL;
    263	}
    264
    265	efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),
    266			     GFP_KERNEL);
    267	if (!efuse)
    268		return -ENOMEM;
    269
    270	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    271	efuse->base = devm_ioremap_resource(dev, res);
    272	if (IS_ERR(efuse->base))
    273		return PTR_ERR(efuse->base);
    274
    275	efuse->clk = devm_clk_get(dev, "pclk_efuse");
    276	if (IS_ERR(efuse->clk))
    277		return PTR_ERR(efuse->clk);
    278
    279	efuse->dev = dev;
    280	if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
    281				 &econfig.size))
    282		econfig.size = resource_size(res);
    283	econfig.reg_read = data;
    284	econfig.priv = efuse;
    285	econfig.dev = efuse->dev;
    286	nvmem = devm_nvmem_register(dev, &econfig);
    287
    288	return PTR_ERR_OR_ZERO(nvmem);
    289}
    290
    291static struct platform_driver rockchip_efuse_driver = {
    292	.probe = rockchip_efuse_probe,
    293	.driver = {
    294		.name = "rockchip-efuse",
    295		.of_match_table = rockchip_efuse_match,
    296	},
    297};
    298
    299module_platform_driver(rockchip_efuse_driver);
    300MODULE_DESCRIPTION("rockchip_efuse driver");
    301MODULE_LICENSE("GPL v2");