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