1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * eFuse driver for Rockchip devices
4 *
5 * Copyright 2017, Theobroma Systems Design und Consulting GmbH
6 * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
7 */
8
9#include <common.h>
10#include <asm/io.h>
11#include <command.h>
12#include <display_options.h>
13#include <dm.h>
14#include <linux/bitops.h>
15#include <linux/delay.h>
16#include <linux/iopoll.h>
17#include <malloc.h>
18#include <misc.h>
19
20#define EFUSE_CTRL		0x0000
21#define RK3036_A_SHIFT		8
22#define RK3036_A_MASK		GENMASK(15, 8)
23#define RK3036_ADDR(n)		((n) << RK3036_A_SHIFT)
24#define RK3128_A_SHIFT		7
25#define RK3128_A_MASK		GENMASK(15, 7)
26#define RK3128_ADDR(n)		((n) << RK3128_A_SHIFT)
27#define RK3288_A_SHIFT		6
28#define RK3288_A_MASK		GENMASK(15, 6)
29#define RK3288_ADDR(n)		((n) << RK3288_A_SHIFT)
30#define RK3399_A_SHIFT		16
31#define RK3399_A_MASK		GENMASK(25, 16)
32#define RK3399_ADDR(n)		((n) << RK3399_A_SHIFT)
33#define RK3399_STROBSFTSEL	BIT(9)
34#define RK3399_RSB		BIT(7)
35#define RK3399_PD		BIT(5)
36#define EFUSE_PGENB		BIT(3)
37#define EFUSE_LOAD		BIT(2)
38#define EFUSE_STROBE		BIT(1)
39#define EFUSE_CSB		BIT(0)
40#define EFUSE_DOUT		0x0004
41#define RK3328_INT_STATUS	0x0018
42#define RK3328_INT_FINISH	BIT(0)
43#define RK3328_DOUT		0x0020
44#define RK3328_AUTO_CTRL	0x0024
45#define RK3328_AUTO_RD		BIT(1)
46#define RK3328_AUTO_ENB		BIT(0)
47
48struct rockchip_efuse_plat {
49	void __iomem *base;
50};
51
52struct rockchip_efuse_data {
53	int (*read)(struct udevice *dev, int offset, void *buf, int size);
54	int offset;
55	int size;
56	int block_size;
57};
58
59#if defined(DEBUG)
60static int dump_efuse(struct cmd_tbl *cmdtp, int flag,
61		      int argc, char *const argv[])
62{
63	struct udevice *dev;
64	u8 data[4];
65	int ret, i;
66
67	ret = uclass_get_device_by_driver(UCLASS_MISC,
68					  DM_DRIVER_GET(rockchip_efuse), &dev);
69	if (ret) {
70		printf("%s: no misc-device found\n", __func__);
71		return 0;
72	}
73
74	for (i = 0; true; i += sizeof(data)) {
75		ret = misc_read(dev, i, &data, sizeof(data));
76		if (ret <= 0)
77			return 0;
78
79		print_buffer(i, data, 1, sizeof(data), sizeof(data));
80	}
81
82	return 0;
83}
84
85U_BOOT_CMD(
86	dump_efuse, 1, 1, dump_efuse,
87	"Dump the content of the efuse",
88	""
89);
90#endif
91
92static int rockchip_rk3036_efuse_read(struct udevice *dev, int offset,
93				      void *buf, int size)
94{
95	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
96	u8 *buffer = buf;
97
98	/* Switch to read mode */
99	writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL);
100	udelay(2);
101
102	while (size--) {
103		clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3036_A_MASK,
104				RK3036_ADDR(offset++));
105		udelay(2);
106		setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
107		udelay(2);
108		*buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
109		clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
110		udelay(2);
111	}
112
113	/* Switch to inactive mode */
114	writel(0x0, efuse->base + EFUSE_CTRL);
115
116	return 0;
117}
118
119static int rockchip_rk3128_efuse_read(struct udevice *dev, int offset,
120				      void *buf, int size)
121{
122	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
123	u8 *buffer = buf;
124
125	/* Switch to read mode */
126	writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL);
127	udelay(2);
128
129	while (size--) {
130		clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3128_A_MASK,
131				RK3128_ADDR(offset++));
132		udelay(2);
133		setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
134		udelay(2);
135		*buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
136		clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
137		udelay(2);
138	}
139
140	/* Switch to inactive mode */
141	writel(0x0, efuse->base + EFUSE_CTRL);
142
143	return 0;
144}
145
146static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
147				      void *buf, int size)
148{
149	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
150	u8 *buffer = buf;
151
152	/* Switch to read mode */
153	writel(EFUSE_CSB, efuse->base + EFUSE_CTRL);
154	writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
155	udelay(2);
156
157	while (size--) {
158		clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3288_A_MASK,
159				RK3288_ADDR(offset++));
160		udelay(2);
161		setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
162		udelay(2);
163		*buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
164		clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
165		udelay(2);
166	}
167
168	/* Switch to standby mode */
169	writel(EFUSE_CSB | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
170
171	return 0;
172}
173
174static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
175				      void *buf, int size)
176{
177	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
178	u32 status, *buffer = buf;
179	int ret;
180
181	while (size--) {
182		writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | RK3399_ADDR(offset++),
183		       efuse->base + RK3328_AUTO_CTRL);
184		udelay(1);
185
186		ret = readl_poll_sleep_timeout(efuse->base + RK3328_INT_STATUS,
187			status, (status & RK3328_INT_FINISH), 1, 50);
188		if (ret)
189			return ret;
190
191		*buffer++ = readl(efuse->base + RK3328_DOUT);
192		writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS);
193	}
194
195	return 0;
196}
197
198static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
199				      void *buf, int size)
200{
201	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
202	u32 *buffer = buf;
203
204	/* Switch to array read mode */
205	writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
206	       efuse->base + EFUSE_CTRL);
207	udelay(1);
208
209	while (size--) {
210		setbits_le32(efuse->base + EFUSE_CTRL,
211			     EFUSE_STROBE | RK3399_ADDR(offset++));
212		udelay(1);
213		*buffer++ = readl(efuse->base + EFUSE_DOUT);
214		clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
215		udelay(1);
216	}
217
218	/* Switch to power-down mode */
219	writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL);
220
221	return 0;
222}
223
224static int rockchip_efuse_read(struct udevice *dev, int offset,
225			       void *buf, int size)
226{
227	const struct rockchip_efuse_data *data =
228		(void *)dev_get_driver_data(dev);
229	u32 block_start, block_end, block_offset, blocks;
230	u8 *buffer;
231	int ret;
232
233	if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
234		return -EINVAL;
235
236	if (!data->read)
237		return -ENOSYS;
238
239	offset += data->offset;
240
241	if (data->block_size <= 1) {
242		ret = data->read(dev, offset, buf, size);
243		goto done;
244	}
245
246	block_start = offset / data->block_size;
247	block_offset = offset % data->block_size;
248	block_end = DIV_ROUND_UP(offset + size, data->block_size);
249	blocks = block_end - block_start;
250
251	buffer = calloc(blocks, data->block_size);
252	if (!buffer)
253		return -ENOMEM;
254
255	ret = data->read(dev, block_start, buffer, blocks);
256	if (!ret)
257		memcpy(buf, buffer + block_offset, size);
258
259	free(buffer);
260
261done:
262	return ret < 0 ? ret : size;
263}
264
265static const struct misc_ops rockchip_efuse_ops = {
266	.read = rockchip_efuse_read,
267};
268
269static int rockchip_efuse_of_to_plat(struct udevice *dev)
270{
271	struct rockchip_efuse_plat *plat = dev_get_plat(dev);
272
273	plat->base = dev_read_addr_ptr(dev);
274
275	return 0;
276}
277
278static const struct rockchip_efuse_data rk3036_data = {
279	.read = rockchip_rk3036_efuse_read,
280	.size = 0x20,
281};
282
283static const struct rockchip_efuse_data rk3128_data = {
284	.read = rockchip_rk3128_efuse_read,
285	.size = 0x40,
286};
287
288static const struct rockchip_efuse_data rk3288_data = {
289	.read = rockchip_rk3288_efuse_read,
290	.size = 0x20,
291};
292
293static const struct rockchip_efuse_data rk3328_data = {
294	.read = rockchip_rk3328_efuse_read,
295	.offset = 0x60,
296	.size = 0x20,
297	.block_size = 4,
298};
299
300static const struct rockchip_efuse_data rk3399_data = {
301	.read = rockchip_rk3399_efuse_read,
302	.size = 0x80,
303	.block_size = 4,
304};
305
306static const struct udevice_id rockchip_efuse_ids[] = {
307	{
308		.compatible = "rockchip,rk3036-efuse",
309		.data = (ulong)&rk3036_data,
310	},
311	{
312		.compatible = "rockchip,rk3066a-efuse",
313		.data = (ulong)&rk3288_data,
314	},
315	{
316		.compatible = "rockchip,rk3128-efuse",
317		.data = (ulong)&rk3128_data,
318	},
319	{
320		.compatible = "rockchip,rk3188-efuse",
321		.data = (ulong)&rk3288_data,
322	},
323	{
324		.compatible = "rockchip,rk3228-efuse",
325		.data = (ulong)&rk3288_data,
326	},
327	{
328		.compatible = "rockchip,rk3288-efuse",
329		.data = (ulong)&rk3288_data,
330	},
331	{
332		.compatible = "rockchip,rk3328-efuse",
333		.data = (ulong)&rk3328_data,
334	},
335	{
336		.compatible = "rockchip,rk3399-efuse",
337		.data = (ulong)&rk3399_data,
338	},
339	{}
340};
341
342U_BOOT_DRIVER(rockchip_efuse) = {
343	.name = "rockchip_efuse",
344	.id = UCLASS_MISC,
345	.of_match = rockchip_efuse_ids,
346	.of_to_plat = rockchip_efuse_of_to_plat,
347	.plat_auto = sizeof(struct rockchip_efuse_plat),
348	.ops = &rockchip_efuse_ops,
349};
350