xref: /rk3399_rockchip-uboot/drivers/misc/rockchip-efuse.c (revision 59a83996bc5a7e2dcbfe446ec23e17b5c26b01d7)
149cd8e85SPhilipp Tomsich /*
249cd8e85SPhilipp Tomsich  * eFuse driver for Rockchip devices
349cd8e85SPhilipp Tomsich  *
449cd8e85SPhilipp Tomsich  * Copyright 2017, Theobroma Systems Design und Consulting GmbH
549cd8e85SPhilipp Tomsich  * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
649cd8e85SPhilipp Tomsich  *
749cd8e85SPhilipp Tomsich  * SPDX-License-Identifier:	GPL-2.0+
849cd8e85SPhilipp Tomsich  */
949cd8e85SPhilipp Tomsich 
1049cd8e85SPhilipp Tomsich #include <common.h>
1149cd8e85SPhilipp Tomsich #include <asm/io.h>
1249cd8e85SPhilipp Tomsich #include <command.h>
1349cd8e85SPhilipp Tomsich #include <display_options.h>
1449cd8e85SPhilipp Tomsich #include <dm.h>
1549cd8e85SPhilipp Tomsich #include <linux/bitops.h>
1649cd8e85SPhilipp Tomsich #include <linux/delay.h>
1749cd8e85SPhilipp Tomsich #include <misc.h>
1849cd8e85SPhilipp Tomsich 
1949cd8e85SPhilipp Tomsich #define RK3399_A_SHIFT          16
2049cd8e85SPhilipp Tomsich #define RK3399_A_MASK           0x3ff
2149cd8e85SPhilipp Tomsich #define RK3399_NFUSES           32
2249cd8e85SPhilipp Tomsich #define RK3399_BYTES_PER_FUSE   4
2349cd8e85SPhilipp Tomsich #define RK3399_STROBSFTSEL      BIT(9)
2449cd8e85SPhilipp Tomsich #define RK3399_RSB              BIT(7)
2549cd8e85SPhilipp Tomsich #define RK3399_PD               BIT(5)
2649cd8e85SPhilipp Tomsich #define RK3399_PGENB            BIT(3)
2749cd8e85SPhilipp Tomsich #define RK3399_LOAD             BIT(2)
2849cd8e85SPhilipp Tomsich #define RK3399_STROBE           BIT(1)
2949cd8e85SPhilipp Tomsich #define RK3399_CSB              BIT(0)
3049cd8e85SPhilipp Tomsich 
31b4fa32f0SFrancis Fan #define RK3288_A_SHIFT          6
32b4fa32f0SFrancis Fan #define RK3288_A_MASK           0x3ff
33b4fa32f0SFrancis Fan #define RK3288_NFUSES           32
34b4fa32f0SFrancis Fan #define RK3288_BYTES_PER_FUSE   1
35b4fa32f0SFrancis Fan #define RK3288_PGENB            BIT(3)
36b4fa32f0SFrancis Fan #define RK3288_LOAD             BIT(2)
37b4fa32f0SFrancis Fan #define RK3288_STROBE           BIT(1)
38b4fa32f0SFrancis Fan #define RK3288_CSB              BIT(0)
39b4fa32f0SFrancis Fan 
40*59a83996SJoseph Chen #define RK3328_INT_STATUS	0x0018
41*59a83996SJoseph Chen #define RK3328_DOUT		0x0020
42*59a83996SJoseph Chen #define RK3328_AUTO_CTRL	0x0024
43*59a83996SJoseph Chen #define RK3328_INT_FINISH	BIT(0)
44*59a83996SJoseph Chen #define RK3328_AUTO_ENB		BIT(0)
45*59a83996SJoseph Chen #define RK3328_AUTO_RD		BIT(1)
46*59a83996SJoseph Chen 
47b4fa32f0SFrancis Fan typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size);
48b4fa32f0SFrancis Fan 
4949cd8e85SPhilipp Tomsich struct rockchip_efuse_regs {
5049cd8e85SPhilipp Tomsich 	u32 ctrl;      /* 0x00  efuse control register */
5149cd8e85SPhilipp Tomsich 	u32 dout;      /* 0x04  efuse data out register */
5249cd8e85SPhilipp Tomsich 	u32 rf;        /* 0x08  efuse redundancy bit used register */
5349cd8e85SPhilipp Tomsich 	u32 _rsvd0;
5449cd8e85SPhilipp Tomsich 	u32 jtag_pass; /* 0x10  JTAG password */
5549cd8e85SPhilipp Tomsich 	u32 strobe_finish_ctrl;
5649cd8e85SPhilipp Tomsich 		       /* 0x14	efuse strobe finish control register */
57*59a83996SJoseph Chen 	u32 int_status;/* 0x18 */
58*59a83996SJoseph Chen 	u32 reserved;  /* 0x1c */
59*59a83996SJoseph Chen 	u32 dout2;     /* 0x20 */
60*59a83996SJoseph Chen 	u32 auto_ctrl; /* 0x24 */
6149cd8e85SPhilipp Tomsich };
6249cd8e85SPhilipp Tomsich 
6349cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata {
6449cd8e85SPhilipp Tomsich 	void __iomem *base;
6549cd8e85SPhilipp Tomsich 	struct clk *clk;
6649cd8e85SPhilipp Tomsich };
6749cd8e85SPhilipp Tomsich 
6849cd8e85SPhilipp Tomsich #if defined(DEBUG)
6949cd8e85SPhilipp Tomsich static int dump_efuses(cmd_tbl_t *cmdtp, int flag,
7049cd8e85SPhilipp Tomsich 		       int argc, char * const argv[])
7149cd8e85SPhilipp Tomsich {
7249cd8e85SPhilipp Tomsich 	/*
7349cd8e85SPhilipp Tomsich 	 * N.B.: This function is tailored towards the RK3399 and assumes that
7449cd8e85SPhilipp Tomsich 	 *       there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to
7549cd8e85SPhilipp Tomsich 	 *       be read.
7649cd8e85SPhilipp Tomsich 	 */
7749cd8e85SPhilipp Tomsich 
7849cd8e85SPhilipp Tomsich 	struct udevice *dev;
79b4fa32f0SFrancis Fan 	u8 fuses[128] = {0};
8049cd8e85SPhilipp Tomsich 	int ret;
8149cd8e85SPhilipp Tomsich 
8249cd8e85SPhilipp Tomsich 	/* retrieve the device */
8349cd8e85SPhilipp Tomsich 	ret = uclass_get_device_by_driver(UCLASS_MISC,
8449cd8e85SPhilipp Tomsich 					  DM_GET_DRIVER(rockchip_efuse), &dev);
8549cd8e85SPhilipp Tomsich 	if (ret) {
8649cd8e85SPhilipp Tomsich 		printf("%s: no misc-device found\n", __func__);
8749cd8e85SPhilipp Tomsich 		return 0;
8849cd8e85SPhilipp Tomsich 	}
8949cd8e85SPhilipp Tomsich 
9049cd8e85SPhilipp Tomsich 	ret = misc_read(dev, 0, &fuses, sizeof(fuses));
9149cd8e85SPhilipp Tomsich 	if (ret) {
9249cd8e85SPhilipp Tomsich 		printf("%s: misc_read failed\n", __func__);
9349cd8e85SPhilipp Tomsich 		return 0;
9449cd8e85SPhilipp Tomsich 	}
9549cd8e85SPhilipp Tomsich 
9649cd8e85SPhilipp Tomsich 	printf("efuse-contents:\n");
9749cd8e85SPhilipp Tomsich 	print_buffer(0, fuses, 1, 128, 16);
9849cd8e85SPhilipp Tomsich 
9949cd8e85SPhilipp Tomsich 	return 0;
10049cd8e85SPhilipp Tomsich }
10149cd8e85SPhilipp Tomsich 
10249cd8e85SPhilipp Tomsich U_BOOT_CMD(
103b4fa32f0SFrancis Fan 	rockchip_dump_efuses, 1, 1, dump_efuses,
10449cd8e85SPhilipp Tomsich 	"Dump the content of the efuses",
10549cd8e85SPhilipp Tomsich 	""
10649cd8e85SPhilipp Tomsich );
10749cd8e85SPhilipp Tomsich #endif
10849cd8e85SPhilipp Tomsich 
10949cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
11049cd8e85SPhilipp Tomsich 				      void *buf, int size)
11149cd8e85SPhilipp Tomsich {
11249cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
11349cd8e85SPhilipp Tomsich 	struct rockchip_efuse_regs *efuse =
11449cd8e85SPhilipp Tomsich 		(struct rockchip_efuse_regs *)plat->base;
11549cd8e85SPhilipp Tomsich 
11649cd8e85SPhilipp Tomsich 	unsigned int addr_start, addr_end, addr_offset;
11749cd8e85SPhilipp Tomsich 	u32 out_value;
11849cd8e85SPhilipp Tomsich 	u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
11949cd8e85SPhilipp Tomsich 	int i = 0;
12049cd8e85SPhilipp Tomsich 	u32 addr;
12149cd8e85SPhilipp Tomsich 
12249cd8e85SPhilipp Tomsich 	addr_start = offset / RK3399_BYTES_PER_FUSE;
12349cd8e85SPhilipp Tomsich 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
12449cd8e85SPhilipp Tomsich 	addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
12549cd8e85SPhilipp Tomsich 
12649cd8e85SPhilipp Tomsich 	/* cap to the size of the efuse block */
12749cd8e85SPhilipp Tomsich 	if (addr_end > RK3399_NFUSES)
12849cd8e85SPhilipp Tomsich 		addr_end = RK3399_NFUSES;
12949cd8e85SPhilipp Tomsich 
13049cd8e85SPhilipp Tomsich 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
13149cd8e85SPhilipp Tomsich 	       &efuse->ctrl);
13249cd8e85SPhilipp Tomsich 	udelay(1);
13349cd8e85SPhilipp Tomsich 	for (addr = addr_start; addr < addr_end; addr++) {
13449cd8e85SPhilipp Tomsich 		setbits_le32(&efuse->ctrl,
13549cd8e85SPhilipp Tomsich 			     RK3399_STROBE | (addr << RK3399_A_SHIFT));
13649cd8e85SPhilipp Tomsich 		udelay(1);
13749cd8e85SPhilipp Tomsich 		out_value = readl(&efuse->dout);
13849cd8e85SPhilipp Tomsich 		clrbits_le32(&efuse->ctrl, RK3399_STROBE);
13949cd8e85SPhilipp Tomsich 		udelay(1);
14049cd8e85SPhilipp Tomsich 
14149cd8e85SPhilipp Tomsich 		memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
14249cd8e85SPhilipp Tomsich 		i += RK3399_BYTES_PER_FUSE;
14349cd8e85SPhilipp Tomsich 	}
14449cd8e85SPhilipp Tomsich 
14549cd8e85SPhilipp Tomsich 	/* Switch to standby mode */
14649cd8e85SPhilipp Tomsich 	writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
14749cd8e85SPhilipp Tomsich 
14849cd8e85SPhilipp Tomsich 	memcpy(buf, bytes + addr_offset, size);
14949cd8e85SPhilipp Tomsich 
15049cd8e85SPhilipp Tomsich 	return 0;
15149cd8e85SPhilipp Tomsich }
15249cd8e85SPhilipp Tomsich 
153b4fa32f0SFrancis Fan static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
154b4fa32f0SFrancis Fan 				      void *buf, int size)
155b4fa32f0SFrancis Fan {
156b4fa32f0SFrancis Fan 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
157b4fa32f0SFrancis Fan 	struct rockchip_efuse_regs *efuse =
158b4fa32f0SFrancis Fan 		(struct rockchip_efuse_regs *)plat->base;
159b4fa32f0SFrancis Fan 	u8 *buffer = buf;
160b4fa32f0SFrancis Fan 	int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE;
161b4fa32f0SFrancis Fan 
162b4fa32f0SFrancis Fan 	if (size > (max_size - offset))
163b4fa32f0SFrancis Fan 		size = max_size - offset;
164b4fa32f0SFrancis Fan 
165b4fa32f0SFrancis Fan 	/* Switch to read mode */
166b4fa32f0SFrancis Fan 	writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl);
167b4fa32f0SFrancis Fan 	udelay(1);
168b4fa32f0SFrancis Fan 
169b4fa32f0SFrancis Fan 	while (size--) {
170b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
171b4fa32f0SFrancis Fan 				(~(RK3288_A_MASK << RK3288_A_SHIFT)),
172b4fa32f0SFrancis Fan 				&efuse->ctrl);
173b4fa32f0SFrancis Fan 		/* set addr */
174b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
175b4fa32f0SFrancis Fan 				((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),
176b4fa32f0SFrancis Fan 				&efuse->ctrl);
177b4fa32f0SFrancis Fan 		udelay(1);
178b4fa32f0SFrancis Fan 		/* strobe low to high */
179b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
180b4fa32f0SFrancis Fan 				RK3288_STROBE, &efuse->ctrl);
181b4fa32f0SFrancis Fan 		ndelay(60);
182b4fa32f0SFrancis Fan 		/* read data */
183b4fa32f0SFrancis Fan 		*buffer++ = readl(&efuse->dout);
184b4fa32f0SFrancis Fan 		/* reset strobe to low */
185b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
186b4fa32f0SFrancis Fan 				(~RK3288_STROBE), &efuse->ctrl);
187b4fa32f0SFrancis Fan 		udelay(1);
188b4fa32f0SFrancis Fan 	}
189b4fa32f0SFrancis Fan 
190b4fa32f0SFrancis Fan 	/* Switch to standby mode */
191b4fa32f0SFrancis Fan 	writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl);
192b4fa32f0SFrancis Fan 
193b4fa32f0SFrancis Fan 	return 0;
194b4fa32f0SFrancis Fan }
195b4fa32f0SFrancis Fan 
196*59a83996SJoseph Chen static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
197*59a83996SJoseph Chen 				      void *buf, int size)
198*59a83996SJoseph Chen {
199*59a83996SJoseph Chen 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
200*59a83996SJoseph Chen 	struct rockchip_efuse_regs *efuse =
201*59a83996SJoseph Chen 		(struct rockchip_efuse_regs *)plat->base;
202*59a83996SJoseph Chen 	unsigned int addr_start, addr_end, addr_offset, addr_len;
203*59a83996SJoseph Chen 	u32 out_value, status;
204*59a83996SJoseph Chen 	u8 *buffer;
205*59a83996SJoseph Chen 	int ret = 0, i = 0, j = 0;
206*59a83996SJoseph Chen 
207*59a83996SJoseph Chen 	/* Max non-secure Byte */
208*59a83996SJoseph Chen 	if (size > 32)
209*59a83996SJoseph Chen 		size = 32;
210*59a83996SJoseph Chen 
211*59a83996SJoseph Chen 	/* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */
212*59a83996SJoseph Chen 	offset += 96;
213*59a83996SJoseph Chen 	addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) /
214*59a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
215*59a83996SJoseph Chen 	addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) /
216*59a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
217*59a83996SJoseph Chen 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
218*59a83996SJoseph Chen 	addr_len = addr_end - addr_start;
219*59a83996SJoseph Chen 
220*59a83996SJoseph Chen 	buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE);
221*59a83996SJoseph Chen 	if (!buffer)
222*59a83996SJoseph Chen 		return -ENOMEM;
223*59a83996SJoseph Chen 
224*59a83996SJoseph Chen 	for (j = 0; j < addr_len; j++) {
225*59a83996SJoseph Chen 		writel(RK3328_AUTO_RD | RK3328_AUTO_ENB |
226*59a83996SJoseph Chen 		       ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
227*59a83996SJoseph Chen 		         &efuse->auto_ctrl);
228*59a83996SJoseph Chen 		udelay(5);
229*59a83996SJoseph Chen 		status = readl(&efuse->int_status);
230*59a83996SJoseph Chen 		if (!(status & RK3328_INT_FINISH)) {
231*59a83996SJoseph Chen 			ret = -EIO;
232*59a83996SJoseph Chen 			goto err;
233*59a83996SJoseph Chen 		}
234*59a83996SJoseph Chen 		out_value = readl(&efuse->dout2);
235*59a83996SJoseph Chen 		writel(RK3328_INT_FINISH, &efuse->int_status);
236*59a83996SJoseph Chen 
237*59a83996SJoseph Chen 		memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE);
238*59a83996SJoseph Chen 		i += RK3399_BYTES_PER_FUSE;
239*59a83996SJoseph Chen 	}
240*59a83996SJoseph Chen 	memcpy(buf, buffer + addr_offset, size);
241*59a83996SJoseph Chen err:
242*59a83996SJoseph Chen 	free(buffer);
243*59a83996SJoseph Chen 
244*59a83996SJoseph Chen 	return ret;
245*59a83996SJoseph Chen }
246*59a83996SJoseph Chen 
24749cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset,
24849cd8e85SPhilipp Tomsich 			       void *buf, int size)
24949cd8e85SPhilipp Tomsich {
250b4fa32f0SFrancis Fan 	EFUSE_READ efuse_read = NULL;
251b4fa32f0SFrancis Fan 
252b4fa32f0SFrancis Fan 	efuse_read = (EFUSE_READ)dev_get_driver_data(dev);
253b4fa32f0SFrancis Fan 	if (!efuse_read)
254b4fa32f0SFrancis Fan 		return -ENOSYS;
255b4fa32f0SFrancis Fan 
256b4fa32f0SFrancis Fan 	return (*efuse_read)(dev, offset, buf, size);
25749cd8e85SPhilipp Tomsich }
25849cd8e85SPhilipp Tomsich 
25949cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = {
26049cd8e85SPhilipp Tomsich 	.read = rockchip_efuse_read,
26149cd8e85SPhilipp Tomsich };
26249cd8e85SPhilipp Tomsich 
26349cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev)
26449cd8e85SPhilipp Tomsich {
26549cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
26649cd8e85SPhilipp Tomsich 
26763a453e6SPhilipp Tomsich 	plat->base = dev_read_addr_ptr(dev);
26849cd8e85SPhilipp Tomsich 	return 0;
26949cd8e85SPhilipp Tomsich }
27049cd8e85SPhilipp Tomsich 
27149cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = {
272b4fa32f0SFrancis Fan 	{
273b4fa32f0SFrancis Fan 		.compatible = "rockchip,rockchip-efuse",
274e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
275b4fa32f0SFrancis Fan 	},
276b4fa32f0SFrancis Fan 	{
277b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3066a-efuse",
278e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
279b4fa32f0SFrancis Fan 	},
280b4fa32f0SFrancis Fan 	{
281b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3188-efuse",
282e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
283b4fa32f0SFrancis Fan 	},
284b4fa32f0SFrancis Fan 	{
285b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk322x-efuse",
286e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
287b4fa32f0SFrancis Fan 	},
288b4fa32f0SFrancis Fan 	{
289*59a83996SJoseph Chen 		.compatible = "rockchip,rk3328-efuse",
290*59a83996SJoseph Chen 		.data = (ulong)&rockchip_rk3328_efuse_read,
291*59a83996SJoseph Chen 	},
292*59a83996SJoseph Chen 	{
293b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3399-efuse",
294b4fa32f0SFrancis Fan 		.data = (ulong)&rockchip_rk3399_efuse_read,
295b4fa32f0SFrancis Fan 	},
29649cd8e85SPhilipp Tomsich 	{}
29749cd8e85SPhilipp Tomsich };
29849cd8e85SPhilipp Tomsich 
29949cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = {
30049cd8e85SPhilipp Tomsich 	.name = "rockchip_efuse",
30149cd8e85SPhilipp Tomsich 	.id = UCLASS_MISC,
30249cd8e85SPhilipp Tomsich 	.of_match = rockchip_efuse_ids,
30349cd8e85SPhilipp Tomsich 	.ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata,
30449cd8e85SPhilipp Tomsich 	.platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata),
30549cd8e85SPhilipp Tomsich 	.ops = &rockchip_efuse_ops,
30649cd8e85SPhilipp Tomsich };
307