xref: /rk3399_rockchip-uboot/drivers/misc/rockchip-efuse.c (revision 758a17b32f4a3d184708cfa2afd68cf2be0460e2)
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 
rk1808_efuse_timing_init(void __iomem * base)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 
rockchip_rk1808_efuse_read(struct udevice * dev,int offset,void * buf,int size)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 
180*758a17b3SFinley Xiao #ifndef CONFIG_SPL_BUILD
rockchip_rk3368_efuse_read(struct udevice * dev,int offset,void * buf,int size)181*758a17b3SFinley Xiao static int rockchip_rk3368_efuse_read(struct udevice *dev, int offset,
182*758a17b3SFinley Xiao 				      void *buf, int size)
183*758a17b3SFinley Xiao {
184*758a17b3SFinley Xiao 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
185*758a17b3SFinley Xiao 	struct rockchip_efuse_regs *efuse =
186*758a17b3SFinley Xiao 		(struct rockchip_efuse_regs *)plat->base;
187*758a17b3SFinley Xiao 	u8 *buffer = buf;
188*758a17b3SFinley Xiao 	struct arm_smccc_res res;
189*758a17b3SFinley Xiao 
190*758a17b3SFinley Xiao 	/* Switch to read mode */
191*758a17b3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
192*758a17b3SFinley Xiao 				 RK3288_LOAD | RK3288_PGENB);
193*758a17b3SFinley Xiao 	udelay(1);
194*758a17b3SFinley Xiao 	while (size--) {
195*758a17b3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
196*758a17b3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 &
197*758a17b3SFinley Xiao 					 (~(RK3288_A_MASK << RK3288_A_SHIFT)));
198*758a17b3SFinley Xiao 		/* set addr */
199*758a17b3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
200*758a17b3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 |
201*758a17b3SFinley Xiao 					 ((offset++ & RK3288_A_MASK) <<
202*758a17b3SFinley Xiao 					  RK3288_A_SHIFT));
203*758a17b3SFinley Xiao 		udelay(1);
204*758a17b3SFinley Xiao 		/* strobe low to high */
205*758a17b3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
206*758a17b3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
207*758a17b3SFinley Xiao 					 res.a1 | RK3288_STROBE);
208*758a17b3SFinley Xiao 		ndelay(60);
209*758a17b3SFinley Xiao 		/* read data */
210*758a17b3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->dout);
211*758a17b3SFinley Xiao 		*buffer++ = res.a1;
212*758a17b3SFinley Xiao 		/* reset strobe to low */
213*758a17b3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
214*758a17b3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
215*758a17b3SFinley Xiao 					 res.a1 & (~RK3288_STROBE));
216*758a17b3SFinley Xiao 		udelay(1);
217*758a17b3SFinley Xiao 	}
218*758a17b3SFinley Xiao 
219*758a17b3SFinley Xiao 	/* Switch to standby mode */
220*758a17b3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
221*758a17b3SFinley Xiao 				 RK3288_PGENB | RK3288_CSB);
222*758a17b3SFinley Xiao 
223*758a17b3SFinley Xiao 	return 0;
224*758a17b3SFinley Xiao }
225*758a17b3SFinley Xiao #endif
226*758a17b3SFinley Xiao 
rockchip_rk3399_efuse_read(struct udevice * dev,int offset,void * buf,int size)22749cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
22849cd8e85SPhilipp Tomsich 				      void *buf, int size)
22949cd8e85SPhilipp Tomsich {
23049cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
23149cd8e85SPhilipp Tomsich 	struct rockchip_efuse_regs *efuse =
23249cd8e85SPhilipp Tomsich 		(struct rockchip_efuse_regs *)plat->base;
23349cd8e85SPhilipp Tomsich 
23449cd8e85SPhilipp Tomsich 	unsigned int addr_start, addr_end, addr_offset;
23549cd8e85SPhilipp Tomsich 	u32 out_value;
23649cd8e85SPhilipp Tomsich 	u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
23749cd8e85SPhilipp Tomsich 	int i = 0;
23849cd8e85SPhilipp Tomsich 	u32 addr;
23949cd8e85SPhilipp Tomsich 
24049cd8e85SPhilipp Tomsich 	addr_start = offset / RK3399_BYTES_PER_FUSE;
24149cd8e85SPhilipp Tomsich 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
24249cd8e85SPhilipp Tomsich 	addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
24349cd8e85SPhilipp Tomsich 
24449cd8e85SPhilipp Tomsich 	/* cap to the size of the efuse block */
24549cd8e85SPhilipp Tomsich 	if (addr_end > RK3399_NFUSES)
24649cd8e85SPhilipp Tomsich 		addr_end = RK3399_NFUSES;
24749cd8e85SPhilipp Tomsich 
24849cd8e85SPhilipp Tomsich 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
24949cd8e85SPhilipp Tomsich 	       &efuse->ctrl);
25049cd8e85SPhilipp Tomsich 	udelay(1);
25149cd8e85SPhilipp Tomsich 	for (addr = addr_start; addr < addr_end; addr++) {
25249cd8e85SPhilipp Tomsich 		setbits_le32(&efuse->ctrl,
25349cd8e85SPhilipp Tomsich 			     RK3399_STROBE | (addr << RK3399_A_SHIFT));
25449cd8e85SPhilipp Tomsich 		udelay(1);
25549cd8e85SPhilipp Tomsich 		out_value = readl(&efuse->dout);
25649cd8e85SPhilipp Tomsich 		clrbits_le32(&efuse->ctrl, RK3399_STROBE);
25749cd8e85SPhilipp Tomsich 		udelay(1);
25849cd8e85SPhilipp Tomsich 
25949cd8e85SPhilipp Tomsich 		memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
26049cd8e85SPhilipp Tomsich 		i += RK3399_BYTES_PER_FUSE;
26149cd8e85SPhilipp Tomsich 	}
26249cd8e85SPhilipp Tomsich 
26349cd8e85SPhilipp Tomsich 	/* Switch to standby mode */
26449cd8e85SPhilipp Tomsich 	writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
26549cd8e85SPhilipp Tomsich 
26649cd8e85SPhilipp Tomsich 	memcpy(buf, bytes + addr_offset, size);
26749cd8e85SPhilipp Tomsich 
26849cd8e85SPhilipp Tomsich 	return 0;
26949cd8e85SPhilipp Tomsich }
27049cd8e85SPhilipp Tomsich 
rockchip_rk3288_efuse_read(struct udevice * dev,int offset,void * buf,int size)271b4fa32f0SFrancis Fan static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
272b4fa32f0SFrancis Fan 				      void *buf, int size)
273b4fa32f0SFrancis Fan {
274b4fa32f0SFrancis Fan 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
275b4fa32f0SFrancis Fan 	struct rockchip_efuse_regs *efuse =
276b4fa32f0SFrancis Fan 		(struct rockchip_efuse_regs *)plat->base;
277b4fa32f0SFrancis Fan 	u8 *buffer = buf;
278b4fa32f0SFrancis Fan 	int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE;
279b4fa32f0SFrancis Fan 
280b4fa32f0SFrancis Fan 	if (size > (max_size - offset))
281b4fa32f0SFrancis Fan 		size = max_size - offset;
282b4fa32f0SFrancis Fan 
283b4fa32f0SFrancis Fan 	/* Switch to read mode */
284b4fa32f0SFrancis Fan 	writel(RK3288_LOAD | RK3288_PGENB, &efuse->ctrl);
285b4fa32f0SFrancis Fan 	udelay(1);
286b4fa32f0SFrancis Fan 
287b4fa32f0SFrancis Fan 	while (size--) {
288b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
289b4fa32f0SFrancis Fan 				(~(RK3288_A_MASK << RK3288_A_SHIFT)),
290b4fa32f0SFrancis Fan 				&efuse->ctrl);
291b4fa32f0SFrancis Fan 		/* set addr */
292b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
293b4fa32f0SFrancis Fan 				((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),
294b4fa32f0SFrancis Fan 				&efuse->ctrl);
295b4fa32f0SFrancis Fan 		udelay(1);
296b4fa32f0SFrancis Fan 		/* strobe low to high */
297b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) |
298b4fa32f0SFrancis Fan 				RK3288_STROBE, &efuse->ctrl);
299b4fa32f0SFrancis Fan 		ndelay(60);
300b4fa32f0SFrancis Fan 		/* read data */
301b4fa32f0SFrancis Fan 		*buffer++ = readl(&efuse->dout);
302b4fa32f0SFrancis Fan 		/* reset strobe to low */
303b4fa32f0SFrancis Fan 		writel(readl(&efuse->ctrl) &
304b4fa32f0SFrancis Fan 				(~RK3288_STROBE), &efuse->ctrl);
305b4fa32f0SFrancis Fan 		udelay(1);
306b4fa32f0SFrancis Fan 	}
307b4fa32f0SFrancis Fan 
308b4fa32f0SFrancis Fan 	/* Switch to standby mode */
309b4fa32f0SFrancis Fan 	writel(RK3288_PGENB | RK3288_CSB, &efuse->ctrl);
310b4fa32f0SFrancis Fan 
311b4fa32f0SFrancis Fan 	return 0;
312b4fa32f0SFrancis Fan }
313b4fa32f0SFrancis Fan 
314492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD
rockchip_rk3288_efuse_secure_read(struct udevice * dev,int offset,void * buf,int size)315492844a3SFinley Xiao static int rockchip_rk3288_efuse_secure_read(struct udevice *dev, int offset,
316492844a3SFinley Xiao 					     void *buf, int size)
317492844a3SFinley Xiao {
318492844a3SFinley Xiao 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
319492844a3SFinley Xiao 	struct rockchip_efuse_regs *efuse =
320492844a3SFinley Xiao 		(struct rockchip_efuse_regs *)plat->base;
321492844a3SFinley Xiao 	u8 *buffer = buf;
322492844a3SFinley Xiao 	int max_size = RK3288_NFUSES * RK3288_BYTES_PER_FUSE;
323492844a3SFinley Xiao 	struct arm_smccc_res res;
324492844a3SFinley Xiao 
325492844a3SFinley Xiao 	if (size > (max_size - offset))
326492844a3SFinley Xiao 		size = max_size - offset;
327492844a3SFinley Xiao 
328492844a3SFinley Xiao 	/* Switch to read mode */
329492844a3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
330492844a3SFinley Xiao 				 RK3288_LOAD | RK3288_PGENB);
331492844a3SFinley Xiao 	udelay(1);
332492844a3SFinley Xiao 	while (size--) {
333492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
334492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 &
335492844a3SFinley Xiao 					 (~(RK3288_A_MASK << RK3288_A_SHIFT)));
336492844a3SFinley Xiao 		/* set addr */
337492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
338492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl, res.a1 |
339492844a3SFinley Xiao 					 ((offset++ & RK3288_A_MASK) <<
340492844a3SFinley Xiao 					  RK3288_A_SHIFT));
341492844a3SFinley Xiao 		udelay(1);
342492844a3SFinley Xiao 		/* strobe low to high */
343492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
344492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
345492844a3SFinley Xiao 					 res.a1 | RK3288_STROBE);
346492844a3SFinley Xiao 		ndelay(60);
347492844a3SFinley Xiao 		/* read data */
348492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->dout);
349492844a3SFinley Xiao 		*buffer++ = res.a1;
350492844a3SFinley Xiao 		/* reset strobe to low */
351492844a3SFinley Xiao 		res = sip_smc_secure_reg_read((ulong)&efuse->ctrl);
352492844a3SFinley Xiao 		sip_smc_secure_reg_write((ulong)&efuse->ctrl,
353492844a3SFinley Xiao 					 res.a1 & (~RK3288_STROBE));
354492844a3SFinley Xiao 		udelay(1);
355492844a3SFinley Xiao 	}
356492844a3SFinley Xiao 
357492844a3SFinley Xiao 	/* Switch to standby mode */
358492844a3SFinley Xiao 	sip_smc_secure_reg_write((ulong)&efuse->ctrl,
359492844a3SFinley Xiao 				 RK3288_PGENB | RK3288_CSB);
360492844a3SFinley Xiao 
361492844a3SFinley Xiao 	return 0;
362492844a3SFinley Xiao }
363492844a3SFinley Xiao #endif
364492844a3SFinley Xiao 
rockchip_rk3328_efuse_read(struct udevice * dev,int offset,void * buf,int size)36559a83996SJoseph Chen static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
36659a83996SJoseph Chen 				      void *buf, int size)
36759a83996SJoseph Chen {
36859a83996SJoseph Chen 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
36959a83996SJoseph Chen 	struct rockchip_efuse_regs *efuse =
37059a83996SJoseph Chen 		(struct rockchip_efuse_regs *)plat->base;
37159a83996SJoseph Chen 	unsigned int addr_start, addr_end, addr_offset, addr_len;
37259a83996SJoseph Chen 	u32 out_value, status;
37359a83996SJoseph Chen 	u8 *buffer;
37459a83996SJoseph Chen 	int ret = 0, i = 0, j = 0;
37559a83996SJoseph Chen 
37659a83996SJoseph Chen 	/* Max non-secure Byte */
37759a83996SJoseph Chen 	if (size > 32)
37859a83996SJoseph Chen 		size = 32;
37959a83996SJoseph Chen 
38059a83996SJoseph Chen 	/* 128 Byte efuse, 96 Byte for secure, 32 Byte for non-secure */
38159a83996SJoseph Chen 	offset += 96;
38259a83996SJoseph Chen 	addr_start = rounddown(offset, RK3399_BYTES_PER_FUSE) /
38359a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
38459a83996SJoseph Chen 	addr_end = roundup(offset + size, RK3399_BYTES_PER_FUSE) /
38559a83996SJoseph Chen 						RK3399_BYTES_PER_FUSE;
38659a83996SJoseph Chen 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
38759a83996SJoseph Chen 	addr_len = addr_end - addr_start;
38859a83996SJoseph Chen 
38959a83996SJoseph Chen 	buffer = calloc(1, sizeof(*buffer) * addr_len * RK3399_BYTES_PER_FUSE);
39059a83996SJoseph Chen 	if (!buffer)
39159a83996SJoseph Chen 		return -ENOMEM;
39259a83996SJoseph Chen 
39359a83996SJoseph Chen 	for (j = 0; j < addr_len; j++) {
39459a83996SJoseph Chen 		writel(RK3328_AUTO_RD | RK3328_AUTO_ENB |
39559a83996SJoseph Chen 		       ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
39659a83996SJoseph Chen 		         &efuse->auto_ctrl);
39759a83996SJoseph Chen 		udelay(5);
39859a83996SJoseph Chen 		status = readl(&efuse->int_status);
39959a83996SJoseph Chen 		if (!(status & RK3328_INT_FINISH)) {
40059a83996SJoseph Chen 			ret = -EIO;
40159a83996SJoseph Chen 			goto err;
40259a83996SJoseph Chen 		}
40359a83996SJoseph Chen 		out_value = readl(&efuse->dout2);
40459a83996SJoseph Chen 		writel(RK3328_INT_FINISH, &efuse->int_status);
40559a83996SJoseph Chen 
40659a83996SJoseph Chen 		memcpy(&buffer[i], &out_value, RK3399_BYTES_PER_FUSE);
40759a83996SJoseph Chen 		i += RK3399_BYTES_PER_FUSE;
40859a83996SJoseph Chen 	}
40959a83996SJoseph Chen 	memcpy(buf, buffer + addr_offset, size);
41059a83996SJoseph Chen err:
41159a83996SJoseph Chen 	free(buffer);
41259a83996SJoseph Chen 
41359a83996SJoseph Chen 	return ret;
41459a83996SJoseph Chen }
41559a83996SJoseph Chen 
rockchip_efuse_read(struct udevice * dev,int offset,void * buf,int size)41649cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset,
41749cd8e85SPhilipp Tomsich 			       void *buf, int size)
41849cd8e85SPhilipp Tomsich {
419b4fa32f0SFrancis Fan 	EFUSE_READ efuse_read = NULL;
420b4fa32f0SFrancis Fan 
421b4fa32f0SFrancis Fan 	efuse_read = (EFUSE_READ)dev_get_driver_data(dev);
422b4fa32f0SFrancis Fan 	if (!efuse_read)
423b4fa32f0SFrancis Fan 		return -ENOSYS;
424b4fa32f0SFrancis Fan 
425b4fa32f0SFrancis Fan 	return (*efuse_read)(dev, offset, buf, size);
42649cd8e85SPhilipp Tomsich }
42749cd8e85SPhilipp Tomsich 
rockchip_efuse_capatiblity(struct udevice * dev,u32 * buf)42840f8ee7aSJoseph Chen static int rockchip_efuse_capatiblity(struct udevice *dev, u32 *buf)
42940f8ee7aSJoseph Chen {
43040f8ee7aSJoseph Chen 	*buf = device_is_compatible(dev, "rockchip,rk3288-secure-efuse") ?
43140f8ee7aSJoseph Chen 	       OTP_S : OTP_NS;
43240f8ee7aSJoseph Chen 
43340f8ee7aSJoseph Chen 	return 0;
43440f8ee7aSJoseph Chen }
43540f8ee7aSJoseph Chen 
rockchip_efuse_ioctl(struct udevice * dev,unsigned long request,void * buf)43640f8ee7aSJoseph Chen static int rockchip_efuse_ioctl(struct udevice *dev, unsigned long request,
43740f8ee7aSJoseph Chen 				void *buf)
43840f8ee7aSJoseph Chen {
43940f8ee7aSJoseph Chen 	int ret = -EINVAL;
44040f8ee7aSJoseph Chen 
44140f8ee7aSJoseph Chen 	switch (request) {
44240f8ee7aSJoseph Chen 	case IOCTL_REQ_CAPABILITY:
44340f8ee7aSJoseph Chen 		ret = rockchip_efuse_capatiblity(dev, buf);
44440f8ee7aSJoseph Chen 		break;
44540f8ee7aSJoseph Chen 	}
44640f8ee7aSJoseph Chen 
44740f8ee7aSJoseph Chen 	return ret;
44840f8ee7aSJoseph Chen }
44940f8ee7aSJoseph Chen 
45049cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = {
45149cd8e85SPhilipp Tomsich 	.read = rockchip_efuse_read,
45240f8ee7aSJoseph Chen 	.ioctl = rockchip_efuse_ioctl,
45349cd8e85SPhilipp Tomsich };
45449cd8e85SPhilipp Tomsich 
rockchip_efuse_ofdata_to_platdata(struct udevice * dev)45549cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev)
45649cd8e85SPhilipp Tomsich {
45749cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
45849cd8e85SPhilipp Tomsich 
45963a453e6SPhilipp Tomsich 	plat->base = dev_read_addr_ptr(dev);
46049cd8e85SPhilipp Tomsich 	return 0;
46149cd8e85SPhilipp Tomsich }
46249cd8e85SPhilipp Tomsich 
46349cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = {
464b4fa32f0SFrancis Fan 	{
465b13cc84cSFinley Xiao 		.compatible = "rockchip,rk1808-efuse",
466b13cc84cSFinley Xiao 		.data = (ulong)&rockchip_rk1808_efuse_read,
467b13cc84cSFinley Xiao 	},
468492844a3SFinley Xiao #ifndef CONFIG_SPL_BUILD
469b13cc84cSFinley Xiao 	{
470492844a3SFinley Xiao 		.compatible = "rockchip,rk3288-secure-efuse",
471492844a3SFinley Xiao 		.data = (ulong)&rockchip_rk3288_efuse_secure_read,
472b4fa32f0SFrancis Fan 	},
473492844a3SFinley Xiao #endif
474b4fa32f0SFrancis Fan 	{
475b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3066a-efuse",
476e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
477b4fa32f0SFrancis Fan 	},
478b4fa32f0SFrancis Fan 	{
479b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3188-efuse",
480e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
481b4fa32f0SFrancis Fan 	},
482b4fa32f0SFrancis Fan 	{
483b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk322x-efuse",
484e9cfb932SCody Xie 		.data = (ulong)&rockchip_rk3288_efuse_read,
485b4fa32f0SFrancis Fan 	},
486b4fa32f0SFrancis Fan 	{
48759a83996SJoseph Chen 		.compatible = "rockchip,rk3328-efuse",
48859a83996SJoseph Chen 		.data = (ulong)&rockchip_rk3328_efuse_read,
48959a83996SJoseph Chen 	},
490*758a17b3SFinley Xiao #ifndef CONFIG_SPL_BUILD
491*758a17b3SFinley Xiao 	{
492*758a17b3SFinley Xiao 		.compatible = "rockchip,rk3368-efuse",
493*758a17b3SFinley Xiao 		.data = (ulong)&rockchip_rk3368_efuse_read,
494*758a17b3SFinley Xiao 	},
495*758a17b3SFinley Xiao #endif
49659a83996SJoseph Chen 	{
497b4fa32f0SFrancis Fan 		.compatible = "rockchip,rk3399-efuse",
498b4fa32f0SFrancis Fan 		.data = (ulong)&rockchip_rk3399_efuse_read,
499b4fa32f0SFrancis Fan 	},
50049cd8e85SPhilipp Tomsich 	{}
50149cd8e85SPhilipp Tomsich };
50249cd8e85SPhilipp Tomsich 
50349cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = {
50449cd8e85SPhilipp Tomsich 	.name = "rockchip_efuse",
50549cd8e85SPhilipp Tomsich 	.id = UCLASS_MISC,
50649cd8e85SPhilipp Tomsich 	.of_match = rockchip_efuse_ids,
50749cd8e85SPhilipp Tomsich 	.ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata,
50849cd8e85SPhilipp Tomsich 	.platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata),
50949cd8e85SPhilipp Tomsich 	.ops = &rockchip_efuse_ops,
51049cd8e85SPhilipp Tomsich };
511