xref: /rk3399_rockchip-uboot/drivers/misc/rockchip-efuse.c (revision 40f8ee7a997d5b116a81d9229b11eb1ebac6ddbf)
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>
15492844a3SFinley Xiao #include <linux/arm-smccc.h>
1649cd8e85SPhilipp Tomsich #include <linux/bitops.h>
1749cd8e85SPhilipp Tomsich #include <linux/delay.h>
1849cd8e85SPhilipp Tomsich #include <misc.h>
19492844a3SFinley Xiao #include <asm/arch/rockchip_smccc.h>
2049cd8e85SPhilipp Tomsich 
21b13cc84cSFinley Xiao #define T_CSB_P_S		0
22b13cc84cSFinley Xiao #define T_PGENB_P_S		0
23b13cc84cSFinley Xiao #define T_LOAD_P_S		0
24b13cc84cSFinley Xiao #define T_ADDR_P_S		0
25b13cc84cSFinley Xiao #define T_STROBE_P_S		(0 + 110) /* 1.1us */
26b13cc84cSFinley Xiao #define T_CSB_P_L		(0 + 110 + 1000 + 20) /* 200ns */
27b13cc84cSFinley Xiao #define T_PGENB_P_L		(0 + 110 + 1000 + 20)
28b13cc84cSFinley Xiao #define T_LOAD_P_L		(0 + 110 + 1000 + 20)
29b13cc84cSFinley Xiao #define T_ADDR_P_L		(0 + 110 + 1000 + 20)
30b13cc84cSFinley Xiao #define T_STROBE_P_L		(0 + 110 + 1000) /* 10us */
31b13cc84cSFinley Xiao #define T_CSB_R_S		0
32b13cc84cSFinley Xiao #define T_PGENB_R_S		0
33b13cc84cSFinley Xiao #define T_LOAD_R_S		0
34b13cc84cSFinley Xiao #define T_ADDR_R_S		2
35b13cc84cSFinley Xiao #define T_STROBE_R_S		(2 + 3)
36b13cc84cSFinley Xiao #define T_CSB_R_L		(2 + 3 + 3 + 3)
37b13cc84cSFinley Xiao #define T_PGENB_R_L		(2 + 3 + 3 + 3)
38b13cc84cSFinley Xiao #define T_LOAD_R_L		(2 + 3 + 3 + 3)
39b13cc84cSFinley Xiao #define T_ADDR_R_L		(2 + 3 + 3 + 2)
40b13cc84cSFinley Xiao #define T_STROBE_R_L		(2 + 3 + 3)
41b13cc84cSFinley Xiao 
42b13cc84cSFinley Xiao #define T_CSB_P			0x28
43b13cc84cSFinley Xiao #define T_PGENB_P		0x2c
44b13cc84cSFinley Xiao #define T_LOAD_P		0x30
45b13cc84cSFinley Xiao #define T_ADDR_P		0x34
46b13cc84cSFinley Xiao #define T_STROBE_P		0x38
47b13cc84cSFinley Xiao #define T_CSB_R			0x3c
48b13cc84cSFinley Xiao #define T_PGENB_R		0x40
49b13cc84cSFinley Xiao #define T_LOAD_R		0x44
50b13cc84cSFinley Xiao #define T_ADDR_R		0x48
51b13cc84cSFinley Xiao #define T_STROBE_R		0x4c
52b13cc84cSFinley Xiao 
53b13cc84cSFinley Xiao #define RK1808_USER_MODE	BIT(0)
54b13cc84cSFinley Xiao #define RK1808_INT_FINISH	BIT(0)
55b13cc84cSFinley Xiao #define RK1808_AUTO_ENB		BIT(0)
56b13cc84cSFinley Xiao #define RK1808_AUTO_RD		BIT(1)
57b13cc84cSFinley Xiao #define RK1808_A_SHIFT		16
58b13cc84cSFinley Xiao #define RK1808_A_MASK		0x3ff
59b13cc84cSFinley Xiao #define RK1808_NBYTES		4
60b13cc84cSFinley Xiao 
6149cd8e85SPhilipp Tomsich #define RK3399_A_SHIFT          16
6249cd8e85SPhilipp Tomsich #define RK3399_A_MASK           0x3ff
6349cd8e85SPhilipp Tomsich #define RK3399_NFUSES           32
6449cd8e85SPhilipp Tomsich #define RK3399_BYTES_PER_FUSE   4
6549cd8e85SPhilipp Tomsich #define RK3399_STROBSFTSEL      BIT(9)
6649cd8e85SPhilipp Tomsich #define RK3399_RSB              BIT(7)
6749cd8e85SPhilipp Tomsich #define RK3399_PD               BIT(5)
6849cd8e85SPhilipp Tomsich #define RK3399_PGENB            BIT(3)
6949cd8e85SPhilipp Tomsich #define RK3399_LOAD             BIT(2)
7049cd8e85SPhilipp Tomsich #define RK3399_STROBE           BIT(1)
7149cd8e85SPhilipp Tomsich #define RK3399_CSB              BIT(0)
7249cd8e85SPhilipp Tomsich 
73b4fa32f0SFrancis Fan #define RK3288_A_SHIFT          6
74b4fa32f0SFrancis Fan #define RK3288_A_MASK           0x3ff
75b4fa32f0SFrancis Fan #define RK3288_NFUSES           32
76b4fa32f0SFrancis Fan #define RK3288_BYTES_PER_FUSE   1
77b4fa32f0SFrancis Fan #define RK3288_PGENB            BIT(3)
78b4fa32f0SFrancis Fan #define RK3288_LOAD             BIT(2)
79b4fa32f0SFrancis Fan #define RK3288_STROBE           BIT(1)
80b4fa32f0SFrancis Fan #define RK3288_CSB              BIT(0)
81b4fa32f0SFrancis Fan 
8259a83996SJoseph Chen #define RK3328_INT_STATUS	0x0018
8359a83996SJoseph Chen #define RK3328_DOUT		0x0020
8459a83996SJoseph Chen #define RK3328_AUTO_CTRL	0x0024
8559a83996SJoseph Chen #define RK3328_INT_FINISH	BIT(0)
8659a83996SJoseph Chen #define RK3328_AUTO_ENB		BIT(0)
8759a83996SJoseph Chen #define RK3328_AUTO_RD		BIT(1)
8859a83996SJoseph Chen 
89b4fa32f0SFrancis Fan typedef int (*EFUSE_READ)(struct udevice *dev, int offset, void *buf, int size);
90b4fa32f0SFrancis Fan 
9149cd8e85SPhilipp Tomsich struct rockchip_efuse_regs {
9249cd8e85SPhilipp Tomsich 	u32 ctrl;      /* 0x00  efuse control register */
9349cd8e85SPhilipp Tomsich 	u32 dout;      /* 0x04  efuse data out register */
9449cd8e85SPhilipp Tomsich 	u32 rf;        /* 0x08  efuse redundancy bit used register */
9549cd8e85SPhilipp Tomsich 	u32 _rsvd0;
9649cd8e85SPhilipp Tomsich 	u32 jtag_pass; /* 0x10  JTAG password */
9749cd8e85SPhilipp Tomsich 	u32 strobe_finish_ctrl;
9849cd8e85SPhilipp Tomsich 		       /* 0x14	efuse strobe finish control register */
9959a83996SJoseph Chen 	u32 int_status;/* 0x18 */
10059a83996SJoseph Chen 	u32 reserved;  /* 0x1c */
10159a83996SJoseph Chen 	u32 dout2;     /* 0x20 */
10259a83996SJoseph Chen 	u32 auto_ctrl; /* 0x24 */
10349cd8e85SPhilipp Tomsich };
10449cd8e85SPhilipp Tomsich 
10549cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata {
10649cd8e85SPhilipp Tomsich 	void __iomem *base;
10749cd8e85SPhilipp Tomsich 	struct clk *clk;
10849cd8e85SPhilipp Tomsich };
10949cd8e85SPhilipp Tomsich 
110b13cc84cSFinley Xiao static void rk1808_efuse_timing_init(void __iomem *base)
111b13cc84cSFinley Xiao {
112b13cc84cSFinley Xiao 	static bool init;
113b13cc84cSFinley Xiao 
114b13cc84cSFinley Xiao 	if (init)
115b13cc84cSFinley Xiao 		return;
116b13cc84cSFinley Xiao 
117b13cc84cSFinley Xiao 	/* enable auto mode */
118b13cc84cSFinley Xiao 	writel(readl(base) & (~RK1808_USER_MODE), base);
119b13cc84cSFinley Xiao 
120b13cc84cSFinley Xiao 	/* setup efuse timing */
121b13cc84cSFinley Xiao 	writel((T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P);
122b13cc84cSFinley Xiao 	writel((T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P);
123b13cc84cSFinley Xiao 	writel((T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P);
124b13cc84cSFinley Xiao 	writel((T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P);
125b13cc84cSFinley Xiao 	writel((T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P);
126b13cc84cSFinley Xiao 	writel((T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R);
127b13cc84cSFinley Xiao 	writel((T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R);
128b13cc84cSFinley Xiao 	writel((T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R);
129b13cc84cSFinley Xiao 	writel((T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R);
130b13cc84cSFinley Xiao 	writel((T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R);
131b13cc84cSFinley Xiao 
132b13cc84cSFinley Xiao 	init = true;
133b13cc84cSFinley Xiao }
134b13cc84cSFinley Xiao 
135b13cc84cSFinley Xiao static int rockchip_rk1808_efuse_read(struct udevice *dev, int offset,
136b13cc84cSFinley Xiao 				      void *buf, int size)
137b13cc84cSFinley Xiao {
138b13cc84cSFinley Xiao 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
139b13cc84cSFinley Xiao 	struct rockchip_efuse_regs *efuse =
140b13cc84cSFinley Xiao 		(struct rockchip_efuse_regs *)plat->base;
141b13cc84cSFinley Xiao 	unsigned int addr_start, addr_end, addr_offset, addr_len;
142b13cc84cSFinley Xiao 	u32 out_value, status;
143b13cc84cSFinley Xiao 	u8 *buffer;
144b13cc84cSFinley Xiao 	int ret = 0, i = 0;
145b13cc84cSFinley Xiao 
146b13cc84cSFinley Xiao 	rk1808_efuse_timing_init(plat->base);
147b13cc84cSFinley Xiao 
148b13cc84cSFinley Xiao 	addr_start = rounddown(offset, RK1808_NBYTES) / RK1808_NBYTES;
149b13cc84cSFinley Xiao 	addr_end = roundup(offset + size, RK1808_NBYTES) / RK1808_NBYTES;
150b13cc84cSFinley Xiao 	addr_offset = offset % RK1808_NBYTES;
151b13cc84cSFinley Xiao 	addr_len = addr_end - addr_start;
152b13cc84cSFinley Xiao 
153b13cc84cSFinley Xiao 	buffer = calloc(1, sizeof(*buffer) * addr_len * RK1808_NBYTES);
154b13cc84cSFinley Xiao 	if (!buffer)
155b13cc84cSFinley Xiao 		return -ENOMEM;
156b13cc84cSFinley Xiao 
157b13cc84cSFinley Xiao 	while (addr_len--) {
158b13cc84cSFinley Xiao 		writel(RK1808_AUTO_RD | RK1808_AUTO_ENB |
159b13cc84cSFinley Xiao 		       ((addr_start++ & RK1808_A_MASK) << RK1808_A_SHIFT),
160b13cc84cSFinley Xiao 		       &efuse->auto_ctrl);
161b13cc84cSFinley Xiao 		udelay(2);
162b13cc84cSFinley Xiao 		status = readl(&efuse->int_status);
163b13cc84cSFinley Xiao 		if (!(status & RK1808_INT_FINISH)) {
164b13cc84cSFinley Xiao 			ret = -EIO;
165b13cc84cSFinley Xiao 			goto err;
166b13cc84cSFinley Xiao 		}
167b13cc84cSFinley Xiao 		out_value = readl(&efuse->dout2);
168b13cc84cSFinley Xiao 		writel(RK1808_INT_FINISH, &efuse->int_status);
169b13cc84cSFinley Xiao 
170b13cc84cSFinley Xiao 		memcpy(&buffer[i], &out_value, RK1808_NBYTES);
171b13cc84cSFinley Xiao 		i += RK1808_NBYTES;
172b13cc84cSFinley Xiao 	}
173b13cc84cSFinley Xiao 	memcpy(buf, buffer + addr_offset, size);
174b13cc84cSFinley Xiao err:
175b13cc84cSFinley Xiao 	kfree(buffer);
176b13cc84cSFinley Xiao 
177b13cc84cSFinley Xiao 	return ret;
178b13cc84cSFinley Xiao }
179b13cc84cSFinley Xiao 
18049cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
18149cd8e85SPhilipp Tomsich 				      void *buf, int size)
18249cd8e85SPhilipp Tomsich {
18349cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
18449cd8e85SPhilipp Tomsich 	struct rockchip_efuse_regs *efuse =
18549cd8e85SPhilipp Tomsich 		(struct rockchip_efuse_regs *)plat->base;
18649cd8e85SPhilipp Tomsich 
18749cd8e85SPhilipp Tomsich 	unsigned int addr_start, addr_end, addr_offset;
18849cd8e85SPhilipp Tomsich 	u32 out_value;
18949cd8e85SPhilipp Tomsich 	u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
19049cd8e85SPhilipp Tomsich 	int i = 0;
19149cd8e85SPhilipp Tomsich 	u32 addr;
19249cd8e85SPhilipp Tomsich 
19349cd8e85SPhilipp Tomsich 	addr_start = offset / RK3399_BYTES_PER_FUSE;
19449cd8e85SPhilipp Tomsich 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
19549cd8e85SPhilipp Tomsich 	addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
19649cd8e85SPhilipp Tomsich 
19749cd8e85SPhilipp Tomsich 	/* cap to the size of the efuse block */
19849cd8e85SPhilipp Tomsich 	if (addr_end > RK3399_NFUSES)
19949cd8e85SPhilipp Tomsich 		addr_end = RK3399_NFUSES;
20049cd8e85SPhilipp Tomsich 
20149cd8e85SPhilipp Tomsich 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
20249cd8e85SPhilipp Tomsich 	       &efuse->ctrl);
20349cd8e85SPhilipp Tomsich 	udelay(1);
20449cd8e85SPhilipp Tomsich 	for (addr = addr_start; addr < addr_end; addr++) {
20549cd8e85SPhilipp Tomsich 		setbits_le32(&efuse->ctrl,
20649cd8e85SPhilipp Tomsich 			     RK3399_STROBE | (addr << RK3399_A_SHIFT));
20749cd8e85SPhilipp Tomsich 		udelay(1);
20849cd8e85SPhilipp Tomsich 		out_value = readl(&efuse->dout);
20949cd8e85SPhilipp Tomsich 		clrbits_le32(&efuse->ctrl, RK3399_STROBE);
21049cd8e85SPhilipp Tomsich 		udelay(1);
21149cd8e85SPhilipp Tomsich 
21249cd8e85SPhilipp Tomsich 		memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
21349cd8e85SPhilipp Tomsich 		i += RK3399_BYTES_PER_FUSE;
21449cd8e85SPhilipp Tomsich 	}
21549cd8e85SPhilipp Tomsich 
21649cd8e85SPhilipp Tomsich 	/* Switch to standby mode */
21749cd8e85SPhilipp Tomsich 	writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
21849cd8e85SPhilipp Tomsich 
21949cd8e85SPhilipp Tomsich 	memcpy(buf, bytes + addr_offset, size);
22049cd8e85SPhilipp Tomsich 
22149cd8e85SPhilipp Tomsich 	return 0;
22249cd8e85SPhilipp Tomsich }
22349cd8e85SPhilipp Tomsich 
224b4fa32f0SFrancis Fan static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
225b4fa32f0SFrancis Fan 				      void *buf, int size)
226b4fa32f0SFrancis Fan {
227b4fa32f0SFrancis Fan 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
228b4fa32f0SFrancis Fan 	struct rockchip_efuse_regs *efuse =
229b4fa32f0SFrancis Fan 		(struct rockchip_efuse_regs *)plat->base;
230b4fa32f0SFrancis Fan 	u8 *buffer = buf;
231b4fa32f0SFrancis Fan 	int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE;
232b4fa32f0SFrancis Fan 
233b4fa32f0SFrancis Fan 	if (size > (max_size - offset))
234b4fa32f0SFrancis Fan 		size = max_size - offset;
235b4fa32f0SFrancis Fan 
236b4fa32f0SFrancis Fan 	/* Switch to read mode */
237b4fa32f0SFrancis Fan 	writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl);
238b4fa32f0SFrancis Fan 	udelay(1);
239b4fa32f0SFrancis Fan 
240b4fa32f0SFrancis Fan 	while (size--) {
241b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
242b4fa32f0SFrancis Fan 				(~(RK3288_A_MASK << RK3288_A_SHIFT)),
243b4fa32f0SFrancis Fan 				&efuse->ctrl);
244b4fa32f0SFrancis Fan 		/* set addr */
245b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
246b4fa32f0SFrancis Fan 				((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),
247b4fa32f0SFrancis Fan 				&efuse->ctrl);
248b4fa32f0SFrancis Fan 		udelay(1);
249b4fa32f0SFrancis Fan 		/* strobe low to high */
250b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
251b4fa32f0SFrancis Fan 				RK3288_STROBE, &efuse->ctrl);
252b4fa32f0SFrancis Fan 		ndelay(60);
253b4fa32f0SFrancis Fan 		/* read data */
254b4fa32f0SFrancis Fan 		*buffer++ = readl(&efuse->dout);
255b4fa32f0SFrancis Fan 		/* reset strobe to low */
256b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
257b4fa32f0SFrancis Fan 				(~RK3288_STROBE), &efuse->ctrl);
258b4fa32f0SFrancis Fan 		udelay(1);
259b4fa32f0SFrancis Fan 	}
260b4fa32f0SFrancis Fan 
261b4fa32f0SFrancis Fan 	/* Switch to standby mode */
262b4fa32f0SFrancis Fan 	writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl);
263b4fa32f0SFrancis Fan 
264b4fa32f0SFrancis Fan 	return 0;
265b4fa32f0SFrancis Fan }
266b4fa32f0SFrancis Fan 
267492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD
268492844a3SFinley Xiao static int rockchip_rk3288_efuse_secure_read(struct udevice *dev, int offset,
269492844a3SFinley Xiao 					     void *buf, int size)
270492844a3SFinley Xiao {
271492844a3SFinley Xiao 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
272492844a3SFinley Xiao 	struct rockchip_efuse_regs *efuse =
273492844a3SFinley Xiao 		(struct rockchip_efuse_regs *)plat->base;
274492844a3SFinley Xiao 	u8 *buffer = buf;
275492844a3SFinley Xiao 	int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE;
276492844a3SFinley Xiao 	struct arm_smccc_res res;
277492844a3SFinley Xiao 
278492844a3SFinley Xiao 	if (size > (max_size - offset))
279492844a3SFinley Xiao 		size = max_size - offset;
280492844a3SFinley Xiao 
281492844a3SFinley Xiao 	/* Switch to read mode */
282492844a3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
283492844a3SFinley Xiao 				 RK3288_LOAD | RK3288_PGENB);
284492844a3SFinley Xiao 	udelay(1);
285492844a3SFinley Xiao 	while (size--) {
286492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
287492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 &
288492844a3SFinley Xiao 					 (~(RK3288_A_MASK << RK3288_A_SHIFT)));
289492844a3SFinley Xiao 		/* set addr */
290492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
291492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 |
292492844a3SFinley Xiao 					 ((offset++ & RK3288_A_MASK) <<
293492844a3SFinley Xiao 					  RK3288_A_SHIFT));
294492844a3SFinley Xiao 		udelay(1);
295492844a3SFinley Xiao 		/* strobe low to high */
296492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
297492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
298492844a3SFinley Xiao 					 res.a1 | RK3288_STROBE);
299492844a3SFinley Xiao 		ndelay(60);
300492844a3SFinley Xiao 		/* read data */
301492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->dout);
302492844a3SFinley Xiao 		*buffer++ = res.a1;
303492844a3SFinley Xiao 		/* reset strobe to low */
304492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
305492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
306492844a3SFinley Xiao 					 res.a1 & (~RK3288_STROBE));
307492844a3SFinley Xiao 		udelay(1);
308492844a3SFinley Xiao 	}
309492844a3SFinley Xiao 
310492844a3SFinley Xiao 	/* Switch to standby mode */
311492844a3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
312492844a3SFinley Xiao 				 RK3288_PGENB | RK3288_CSB);
313492844a3SFinley Xiao 
314492844a3SFinley Xiao 	return 0;
315492844a3SFinley Xiao }
316492844a3SFinley Xiao #endif
317492844a3SFinley Xiao 
31859a83996SJoseph Chen static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
31959a83996SJoseph Chen 				      void *buf, int size)
32059a83996SJoseph Chen {
32159a83996SJoseph Chen 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
32259a83996SJoseph Chen 	struct rockchip_efuse_regs *efuse =
32359a83996SJoseph Chen 		(struct rockchip_efuse_regs *)plat->base;
32459a83996SJoseph Chen 	unsigned int addr_start, addr_end, addr_offset, addr_len;
32559a83996SJoseph Chen 	u32 out_value, status;
32659a83996SJoseph Chen 	u8 *buffer;
32759a83996SJoseph Chen 	int ret = 0, i = 0, j = 0;
32859a83996SJoseph Chen 
32959a83996SJoseph Chen 	/* Max non-secure Byte */
33059a83996SJoseph Chen 	if (size > 32)
33159a83996SJoseph Chen 		size = 32;
33259a83996SJoseph Chen 
33359a83996SJoseph Chen 	/* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */
33459a83996SJoseph Chen 	offset += 96;
33559a83996SJoseph Chen 	addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) /
33659a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
33759a83996SJoseph Chen 	addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) /
33859a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
33959a83996SJoseph Chen 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
34059a83996SJoseph Chen 	addr_len = addr_end - addr_start;
34159a83996SJoseph Chen 
34259a83996SJoseph Chen 	buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE);
34359a83996SJoseph Chen 	if (!buffer)
34459a83996SJoseph Chen 		return -ENOMEM;
34559a83996SJoseph Chen 
34659a83996SJoseph Chen 	for (j = 0; j < addr_len; j++) {
34759a83996SJoseph Chen 		writel(RK3328_AUTO_RD | RK3328_AUTO_ENB |
34859a83996SJoseph Chen 		       ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
34959a83996SJoseph Chen 		         &efuse->auto_ctrl);
35059a83996SJoseph Chen 		udelay(5);
35159a83996SJoseph Chen 		status = readl(&efuse->int_status);
35259a83996SJoseph Chen 		if (!(status & RK3328_INT_FINISH)) {
35359a83996SJoseph Chen 			ret = -EIO;
35459a83996SJoseph Chen 			goto err;
35559a83996SJoseph Chen 		}
35659a83996SJoseph Chen 		out_value = readl(&efuse->dout2);
35759a83996SJoseph Chen 		writel(RK3328_INT_FINISH, &efuse->int_status);
35859a83996SJoseph Chen 
35959a83996SJoseph Chen 		memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE);
36059a83996SJoseph Chen 		i += RK3399_BYTES_PER_FUSE;
36159a83996SJoseph Chen 	}
36259a83996SJoseph Chen 	memcpy(buf, buffer + addr_offset, size);
36359a83996SJoseph Chen err:
36459a83996SJoseph Chen 	free(buffer);
36559a83996SJoseph Chen 
36659a83996SJoseph Chen 	return ret;
36759a83996SJoseph Chen }
36859a83996SJoseph Chen 
36949cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset,
37049cd8e85SPhilipp Tomsich 			       void *buf, int size)
37149cd8e85SPhilipp Tomsich {
372b4fa32f0SFrancis Fan 	EFUSE_READ efuse_read = NULL;
373b4fa32f0SFrancis Fan 
374b4fa32f0SFrancis Fan 	efuse_read = (EFUSE_READ)dev_get_driver_data(dev);
375b4fa32f0SFrancis Fan 	if (!efuse_read)
376b4fa32f0SFrancis Fan 		return -ENOSYS;
377b4fa32f0SFrancis Fan 
378b4fa32f0SFrancis Fan 	return (*efuse_read)(dev, offset, buf, size);
37949cd8e85SPhilipp Tomsich }
38049cd8e85SPhilipp Tomsich 
381*40f8ee7aSJoseph Chen static int rockchip_efuse_capatiblity(struct udevice *dev, u32 *buf)
382*40f8ee7aSJoseph Chen {
383*40f8ee7aSJoseph Chen 	*buf = device_is_compatible(dev, "rockchip,rk3288-secure-efuse") ?
384*40f8ee7aSJoseph Chen 	       OTP_S : OTP_NS;
385*40f8ee7aSJoseph Chen 
386*40f8ee7aSJoseph Chen 	return 0;
387*40f8ee7aSJoseph Chen }
388*40f8ee7aSJoseph Chen 
389*40f8ee7aSJoseph Chen static int rockchip_efuse_ioctl(struct udevice *dev, unsigned long request,
390*40f8ee7aSJoseph Chen 				void *buf)
391*40f8ee7aSJoseph Chen {
392*40f8ee7aSJoseph Chen 	int ret = -EINVAL;
393*40f8ee7aSJoseph Chen 
394*40f8ee7aSJoseph Chen 	switch (request) {
395*40f8ee7aSJoseph Chen 	case IOCTL_REQ_CAPABILITY:
396*40f8ee7aSJoseph Chen 		ret = rockchip_efuse_capatiblity(dev, buf);
397*40f8ee7aSJoseph Chen 		break;
398*40f8ee7aSJoseph Chen 	}
399*40f8ee7aSJoseph Chen 
400*40f8ee7aSJoseph Chen 	return ret;
401*40f8ee7aSJoseph Chen }
402*40f8ee7aSJoseph Chen 
40349cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = {
40449cd8e85SPhilipp Tomsich 	.read = rockchip_efuse_read,
405*40f8ee7aSJoseph Chen 	.ioctl = rockchip_efuse_ioctl,
40649cd8e85SPhilipp Tomsich };
40749cd8e85SPhilipp Tomsich 
40849cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev)
40949cd8e85SPhilipp Tomsich {
41049cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
41149cd8e85SPhilipp Tomsich 
41263a453e6SPhilipp Tomsich 	plat->base = dev_read_addr_ptr(dev);
41349cd8e85SPhilipp Tomsich 	return 0;
41449cd8e85SPhilipp Tomsich }
41549cd8e85SPhilipp Tomsich 
41649cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = {
417b4fa32f0SFrancis Fan 	{
418b13cc84cSFinley Xiao 		.compatible = "rockchip,rk1808-efuse",
419b13cc84cSFinley Xiao 		.data = (ulong)&rockchip_rk1808_efuse_read,
420b13cc84cSFinley Xiao 	},
421492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD
422b13cc84cSFinley Xiao 	{
423492844a3SFinley Xiao 		.compatible = "rockchip,rk3288-secure-efuse",
424492844a3SFinley Xiao 		.data = (ulong)&rockchip_rk3288_efuse_secure_read,
425b4fa32f0SFrancis Fan 	},
426492844a3SFinley Xiao #endif
427b4fa32f0SFrancis Fan 	{
428b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3066a-efuse",
429e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
430b4fa32f0SFrancis Fan 	},
431b4fa32f0SFrancis Fan 	{
432b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3188-efuse",
433e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
434b4fa32f0SFrancis Fan 	},
435b4fa32f0SFrancis Fan 	{
436b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk322x-efuse",
437e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
438b4fa32f0SFrancis Fan 	},
439b4fa32f0SFrancis Fan 	{
44059a83996SJoseph Chen 		.compatible = "rockchip,rk3328-efuse",
44159a83996SJoseph Chen 		.data = (ulong)&rockchip_rk3328_efuse_read,
44259a83996SJoseph Chen 	},
44359a83996SJoseph Chen 	{
444b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3399-efuse",
445b4fa32f0SFrancis Fan 		.data = (ulong)&rockchip_rk3399_efuse_read,
446b4fa32f0SFrancis Fan 	},
44749cd8e85SPhilipp Tomsich 	{}
44849cd8e85SPhilipp Tomsich };
44949cd8e85SPhilipp Tomsich 
45049cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = {
45149cd8e85SPhilipp Tomsich 	.name = "rockchip_efuse",
45249cd8e85SPhilipp Tomsich 	.id = UCLASS_MISC,
45349cd8e85SPhilipp Tomsich 	.of_match = rockchip_efuse_ids,
45449cd8e85SPhilipp Tomsich 	.ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata,
45549cd8e85SPhilipp Tomsich 	.platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata),
45649cd8e85SPhilipp Tomsich 	.ops = &rockchip_efuse_ops,
45749cd8e85SPhilipp Tomsich };
458