xref: /rk3399_rockchip-uboot/drivers/misc/rockchip-efuse.c (revision 49cd8e85eb70b5af4a27c7e006fbdd46d5796b2c)
1*49cd8e85SPhilipp Tomsich /*
2*49cd8e85SPhilipp Tomsich  * eFuse driver for Rockchip devices
3*49cd8e85SPhilipp Tomsich  *
4*49cd8e85SPhilipp Tomsich  * Copyright 2017, Theobroma Systems Design und Consulting GmbH
5*49cd8e85SPhilipp Tomsich  * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
6*49cd8e85SPhilipp Tomsich  *
7*49cd8e85SPhilipp Tomsich  * SPDX-License-Identifier:	GPL-2.0+
8*49cd8e85SPhilipp Tomsich  */
9*49cd8e85SPhilipp Tomsich 
10*49cd8e85SPhilipp Tomsich #include <common.h>
11*49cd8e85SPhilipp Tomsich #include <asm/io.h>
12*49cd8e85SPhilipp Tomsich #include <command.h>
13*49cd8e85SPhilipp Tomsich #include <display_options.h>
14*49cd8e85SPhilipp Tomsich #include <dm.h>
15*49cd8e85SPhilipp Tomsich #include <linux/bitops.h>
16*49cd8e85SPhilipp Tomsich #include <linux/delay.h>
17*49cd8e85SPhilipp Tomsich #include <misc.h>
18*49cd8e85SPhilipp Tomsich 
19*49cd8e85SPhilipp Tomsich #define RK3399_A_SHIFT          16
20*49cd8e85SPhilipp Tomsich #define RK3399_A_MASK           0x3ff
21*49cd8e85SPhilipp Tomsich #define RK3399_NFUSES           32
22*49cd8e85SPhilipp Tomsich #define RK3399_BYTES_PER_FUSE   4
23*49cd8e85SPhilipp Tomsich #define RK3399_STROBSFTSEL      BIT(9)
24*49cd8e85SPhilipp Tomsich #define RK3399_RSB              BIT(7)
25*49cd8e85SPhilipp Tomsich #define RK3399_PD               BIT(5)
26*49cd8e85SPhilipp Tomsich #define RK3399_PGENB            BIT(3)
27*49cd8e85SPhilipp Tomsich #define RK3399_LOAD             BIT(2)
28*49cd8e85SPhilipp Tomsich #define RK3399_STROBE           BIT(1)
29*49cd8e85SPhilipp Tomsich #define RK3399_CSB              BIT(0)
30*49cd8e85SPhilipp Tomsich 
31*49cd8e85SPhilipp Tomsich struct rockchip_efuse_regs {
32*49cd8e85SPhilipp Tomsich 	u32 ctrl;      /* 0x00  efuse control register */
33*49cd8e85SPhilipp Tomsich 	u32 dout;      /* 0x04  efuse data out register */
34*49cd8e85SPhilipp Tomsich 	u32 rf;        /* 0x08  efuse redundancy bit used register */
35*49cd8e85SPhilipp Tomsich 	u32 _rsvd0;
36*49cd8e85SPhilipp Tomsich 	u32 jtag_pass; /* 0x10  JTAG password */
37*49cd8e85SPhilipp Tomsich 	u32 strobe_finish_ctrl;
38*49cd8e85SPhilipp Tomsich 		       /* 0x14	efuse strobe finish control register */
39*49cd8e85SPhilipp Tomsich };
40*49cd8e85SPhilipp Tomsich 
41*49cd8e85SPhilipp Tomsich struct rockchip_efuse_platdata {
42*49cd8e85SPhilipp Tomsich 	void __iomem *base;
43*49cd8e85SPhilipp Tomsich 	struct clk *clk;
44*49cd8e85SPhilipp Tomsich };
45*49cd8e85SPhilipp Tomsich 
46*49cd8e85SPhilipp Tomsich #if defined(DEBUG)
47*49cd8e85SPhilipp Tomsich static int dump_efuses(cmd_tbl_t *cmdtp, int flag,
48*49cd8e85SPhilipp Tomsich 		       int argc, char * const argv[])
49*49cd8e85SPhilipp Tomsich {
50*49cd8e85SPhilipp Tomsich 	/*
51*49cd8e85SPhilipp Tomsich 	 * N.B.: This function is tailored towards the RK3399 and assumes that
52*49cd8e85SPhilipp Tomsich 	 *       there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to
53*49cd8e85SPhilipp Tomsich 	 *       be read.
54*49cd8e85SPhilipp Tomsich 	 */
55*49cd8e85SPhilipp Tomsich 
56*49cd8e85SPhilipp Tomsich 	struct udevice *dev;
57*49cd8e85SPhilipp Tomsich 	u8 fuses[128];
58*49cd8e85SPhilipp Tomsich 	int ret;
59*49cd8e85SPhilipp Tomsich 
60*49cd8e85SPhilipp Tomsich 	/* retrieve the device */
61*49cd8e85SPhilipp Tomsich 	ret = uclass_get_device_by_driver(UCLASS_MISC,
62*49cd8e85SPhilipp Tomsich 					  DM_GET_DRIVER(rockchip_efuse), &dev);
63*49cd8e85SPhilipp Tomsich 	if (ret) {
64*49cd8e85SPhilipp Tomsich 		printf("%s: no misc-device found\n", __func__);
65*49cd8e85SPhilipp Tomsich 		return 0;
66*49cd8e85SPhilipp Tomsich 	}
67*49cd8e85SPhilipp Tomsich 
68*49cd8e85SPhilipp Tomsich 	ret = misc_read(dev, 0, &fuses, sizeof(fuses));
69*49cd8e85SPhilipp Tomsich 	if (ret) {
70*49cd8e85SPhilipp Tomsich 		printf("%s: misc_read failed\n", __func__);
71*49cd8e85SPhilipp Tomsich 		return 0;
72*49cd8e85SPhilipp Tomsich 	}
73*49cd8e85SPhilipp Tomsich 
74*49cd8e85SPhilipp Tomsich 	printf("efuse-contents:\n");
75*49cd8e85SPhilipp Tomsich 	print_buffer(0, fuses, 1, 128, 16);
76*49cd8e85SPhilipp Tomsich 
77*49cd8e85SPhilipp Tomsich 	return 0;
78*49cd8e85SPhilipp Tomsich }
79*49cd8e85SPhilipp Tomsich 
80*49cd8e85SPhilipp Tomsich U_BOOT_CMD(
81*49cd8e85SPhilipp Tomsich 	rk3399_dump_efuses, 1, 1, dump_efuses,
82*49cd8e85SPhilipp Tomsich 	"Dump the content of the efuses",
83*49cd8e85SPhilipp Tomsich 	""
84*49cd8e85SPhilipp Tomsich );
85*49cd8e85SPhilipp Tomsich #endif
86*49cd8e85SPhilipp Tomsich 
87*49cd8e85SPhilipp Tomsich static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
88*49cd8e85SPhilipp Tomsich 				      void *buf, int size)
89*49cd8e85SPhilipp Tomsich {
90*49cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
91*49cd8e85SPhilipp Tomsich 	struct rockchip_efuse_regs *efuse =
92*49cd8e85SPhilipp Tomsich 		(struct rockchip_efuse_regs *)plat->base;
93*49cd8e85SPhilipp Tomsich 
94*49cd8e85SPhilipp Tomsich 	unsigned int addr_start, addr_end, addr_offset;
95*49cd8e85SPhilipp Tomsich 	u32 out_value;
96*49cd8e85SPhilipp Tomsich 	u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
97*49cd8e85SPhilipp Tomsich 	int i = 0;
98*49cd8e85SPhilipp Tomsich 	u32 addr;
99*49cd8e85SPhilipp Tomsich 
100*49cd8e85SPhilipp Tomsich 	addr_start = offset / RK3399_BYTES_PER_FUSE;
101*49cd8e85SPhilipp Tomsich 	addr_offset = offset % RK3399_BYTES_PER_FUSE;
102*49cd8e85SPhilipp Tomsich 	addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
103*49cd8e85SPhilipp Tomsich 
104*49cd8e85SPhilipp Tomsich 	/* cap to the size of the efuse block */
105*49cd8e85SPhilipp Tomsich 	if (addr_end > RK3399_NFUSES)
106*49cd8e85SPhilipp Tomsich 		addr_end = RK3399_NFUSES;
107*49cd8e85SPhilipp Tomsich 
108*49cd8e85SPhilipp Tomsich 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
109*49cd8e85SPhilipp Tomsich 	       &efuse->ctrl);
110*49cd8e85SPhilipp Tomsich 	udelay(1);
111*49cd8e85SPhilipp Tomsich 	for (addr = addr_start; addr < addr_end; addr++) {
112*49cd8e85SPhilipp Tomsich 		setbits_le32(&efuse->ctrl,
113*49cd8e85SPhilipp Tomsich 			     RK3399_STROBE | (addr << RK3399_A_SHIFT));
114*49cd8e85SPhilipp Tomsich 		udelay(1);
115*49cd8e85SPhilipp Tomsich 		out_value = readl(&efuse->dout);
116*49cd8e85SPhilipp Tomsich 		clrbits_le32(&efuse->ctrl, RK3399_STROBE);
117*49cd8e85SPhilipp Tomsich 		udelay(1);
118*49cd8e85SPhilipp Tomsich 
119*49cd8e85SPhilipp Tomsich 		memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
120*49cd8e85SPhilipp Tomsich 		i += RK3399_BYTES_PER_FUSE;
121*49cd8e85SPhilipp Tomsich 	}
122*49cd8e85SPhilipp Tomsich 
123*49cd8e85SPhilipp Tomsich 	/* Switch to standby mode */
124*49cd8e85SPhilipp Tomsich 	writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
125*49cd8e85SPhilipp Tomsich 
126*49cd8e85SPhilipp Tomsich 	memcpy(buf, bytes + addr_offset, size);
127*49cd8e85SPhilipp Tomsich 
128*49cd8e85SPhilipp Tomsich 	return 0;
129*49cd8e85SPhilipp Tomsich }
130*49cd8e85SPhilipp Tomsich 
131*49cd8e85SPhilipp Tomsich static int rockchip_efuse_read(struct udevice *dev, int offset,
132*49cd8e85SPhilipp Tomsich 			       void *buf, int size)
133*49cd8e85SPhilipp Tomsich {
134*49cd8e85SPhilipp Tomsich 	return rockchip_rk3399_efuse_read(dev, offset, buf, size);
135*49cd8e85SPhilipp Tomsich }
136*49cd8e85SPhilipp Tomsich 
137*49cd8e85SPhilipp Tomsich static const struct misc_ops rockchip_efuse_ops = {
138*49cd8e85SPhilipp Tomsich 	.read = rockchip_efuse_read,
139*49cd8e85SPhilipp Tomsich };
140*49cd8e85SPhilipp Tomsich 
141*49cd8e85SPhilipp Tomsich static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev)
142*49cd8e85SPhilipp Tomsich {
143*49cd8e85SPhilipp Tomsich 	struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
144*49cd8e85SPhilipp Tomsich 
145*49cd8e85SPhilipp Tomsich 	plat->base = (void *)devfdt_get_addr(dev);
146*49cd8e85SPhilipp Tomsich 	return 0;
147*49cd8e85SPhilipp Tomsich }
148*49cd8e85SPhilipp Tomsich 
149*49cd8e85SPhilipp Tomsich static const struct udevice_id rockchip_efuse_ids[] = {
150*49cd8e85SPhilipp Tomsich 	{ .compatible = "rockchip,rk3399-efuse" },
151*49cd8e85SPhilipp Tomsich 	{}
152*49cd8e85SPhilipp Tomsich };
153*49cd8e85SPhilipp Tomsich 
154*49cd8e85SPhilipp Tomsich U_BOOT_DRIVER(rockchip_efuse) = {
155*49cd8e85SPhilipp Tomsich 	.name = "rockchip_efuse",
156*49cd8e85SPhilipp Tomsich 	.id = UCLASS_MISC,
157*49cd8e85SPhilipp Tomsich 	.of_match = rockchip_efuse_ids,
158*49cd8e85SPhilipp Tomsich 	.ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata,
159*49cd8e85SPhilipp Tomsich 	.platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata),
160*49cd8e85SPhilipp Tomsich 	.ops = &rockchip_efuse_ops,
161*49cd8e85SPhilipp Tomsich };
162