blob: 2f96b79ea407dd56ba1bc2ddcd4857f9168273ab [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Philipp Tomsich49cd8e82017-05-05 19:21:38 +02002/*
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>
Philipp Tomsich49cd8e82017-05-05 19:21:38 +02007 */
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>
Jonas Karlmanb0828cf2023-02-22 22:44:40 +000016#include <linux/iopoll.h>
Jonas Karlman433260a2023-02-22 22:44:39 +000017#include <malloc.h>
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020018#include <misc.h>
19
Jonas Karlman433260a2023-02-22 22:44:39 +000020#define EFUSE_CTRL 0x0000
Jonas Karlmandd5684b2023-02-22 22:44:40 +000021#define RK3036_A_SHIFT 8
22#define RK3036_A_MASK GENMASK(15, 8)
23#define RK3036_ADDR(n) ((n) << RK3036_A_SHIFT)
Jonas Karlman16e8afa2023-02-22 22:44:40 +000024#define RK3128_A_SHIFT 7
25#define RK3128_A_MASK GENMASK(15, 7)
26#define RK3128_ADDR(n) ((n) << RK3128_A_SHIFT)
Jonas Karlman6f95b3e2023-02-22 22:44:40 +000027#define RK3288_A_SHIFT 6
28#define RK3288_A_MASK GENMASK(15, 6)
29#define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT)
Jonas Karlman433260a2023-02-22 22:44:39 +000030#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)
Jonas Karlman6f95b3e2023-02-22 22:44:40 +000036#define EFUSE_PGENB BIT(3)
37#define EFUSE_LOAD BIT(2)
38#define EFUSE_STROBE BIT(1)
39#define EFUSE_CSB BIT(0)
Jonas Karlman433260a2023-02-22 22:44:39 +000040#define EFUSE_DOUT 0x0004
Jonas Karlmanb0828cf2023-02-22 22:44:40 +000041#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)
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020047
Simon Glass8a8d24b2020-12-03 16:55:23 -070048struct rockchip_efuse_plat {
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020049 void __iomem *base;
Jonas Karlman433260a2023-02-22 22:44:39 +000050};
51
52struct rockchip_efuse_data {
53 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlmanb0828cf2023-02-22 22:44:40 +000054 int offset;
Jonas Karlman433260a2023-02-22 22:44:39 +000055 int size;
56 int block_size;
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020057};
58
59#if defined(DEBUG)
Jonas Karlman433260a2023-02-22 22:44:39 +000060static int dump_efuse(struct cmd_tbl *cmdtp, int flag,
61 int argc, char *const argv[])
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020062{
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020063 struct udevice *dev;
Jonas Karlman433260a2023-02-22 22:44:39 +000064 u8 data[4];
65 int ret, i;
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020066
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020067 ret = uclass_get_device_by_driver(UCLASS_MISC,
Simon Glass65e25be2020-12-28 20:34:56 -070068 DM_DRIVER_GET(rockchip_efuse), &dev);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020069 if (ret) {
70 printf("%s: no misc-device found\n", __func__);
71 return 0;
72 }
73
Jonas Karlman433260a2023-02-22 22:44:39 +000074 for (i = 0; true; i += sizeof(data)) {
75 ret = misc_read(dev, i, &data, sizeof(data));
John Keepinga48d1352023-03-27 12:01:09 +010076 if (ret <= 0)
Jonas Karlman433260a2023-02-22 22:44:39 +000077 return 0;
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020078
Jonas Karlman433260a2023-02-22 22:44:39 +000079 print_buffer(i, data, 1, sizeof(data), sizeof(data));
80 }
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020081
82 return 0;
83}
84
85U_BOOT_CMD(
Jonas Karlman433260a2023-02-22 22:44:39 +000086 dump_efuse, 1, 1, dump_efuse,
87 "Dump the content of the efuse",
Philipp Tomsich49cd8e82017-05-05 19:21:38 +020088 ""
89);
90#endif
91
Jonas Karlmandd5684b2023-02-22 22:44:40 +000092static 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
Jonas Karlman16e8afa2023-02-22 22:44:40 +0000119static 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
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000146static 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
Jonas Karlmanb0828cf2023-02-22 22:44:40 +0000174static 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
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200198static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
199 void *buf, int size)
200{
Jonas Karlman433260a2023-02-22 22:44:39 +0000201 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
202 u32 *buffer = buf;
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200203
Jonas Karlman433260a2023-02-22 22:44:39 +0000204 /* Switch to array read mode */
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000205 writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
Jonas Karlman433260a2023-02-22 22:44:39 +0000206 efuse->base + EFUSE_CTRL);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200207 udelay(1);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200208
Jonas Karlman433260a2023-02-22 22:44:39 +0000209 while (size--) {
210 setbits_le32(efuse->base + EFUSE_CTRL,
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000211 EFUSE_STROBE | RK3399_ADDR(offset++));
Jonas Karlman433260a2023-02-22 22:44:39 +0000212 udelay(1);
213 *buffer++ = readl(efuse->base + EFUSE_DOUT);
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000214 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
Jonas Karlman433260a2023-02-22 22:44:39 +0000215 udelay(1);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200216 }
217
Jonas Karlman433260a2023-02-22 22:44:39 +0000218 /* Switch to power-down mode */
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000219 writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200220
221 return 0;
222}
223
224static int rockchip_efuse_read(struct udevice *dev, int offset,
225 void *buf, int size)
226{
Jonas Karlman433260a2023-02-22 22:44:39 +0000227 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
Jonas Karlmanb0828cf2023-02-22 22:44:40 +0000239 offset += data->offset;
240
John Keepinga48d1352023-03-27 12:01:09 +0100241 if (data->block_size <= 1) {
242 ret = data->read(dev, offset, buf, size);
243 goto done;
244 }
Jonas Karlman433260a2023-02-22 22:44:39 +0000245
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);
John Keepinga48d1352023-03-27 12:01:09 +0100260
261done:
262 return ret < 0 ? ret : size;
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200263}
264
265static const struct misc_ops rockchip_efuse_ops = {
266 .read = rockchip_efuse_read,
267};
268
Simon Glassd1998a92020-12-03 16:55:21 -0700269static int rockchip_efuse_of_to_plat(struct udevice *dev)
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200270{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700271 struct rockchip_efuse_plat *plat = dev_get_plat(dev);
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200272
Philipp Tomsichf6230a02017-09-12 17:32:26 +0200273 plat->base = dev_read_addr_ptr(dev);
Jonas Karlman433260a2023-02-22 22:44:39 +0000274
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200275 return 0;
276}
277
Jonas Karlmandd5684b2023-02-22 22:44:40 +0000278static const struct rockchip_efuse_data rk3036_data = {
279 .read = rockchip_rk3036_efuse_read,
280 .size = 0x20,
281};
282
Jonas Karlman16e8afa2023-02-22 22:44:40 +0000283static const struct rockchip_efuse_data rk3128_data = {
284 .read = rockchip_rk3128_efuse_read,
285 .size = 0x40,
286};
287
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000288static const struct rockchip_efuse_data rk3288_data = {
289 .read = rockchip_rk3288_efuse_read,
290 .size = 0x20,
291};
292
Jonas Karlmanb0828cf2023-02-22 22:44:40 +0000293static 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
Jonas Karlman433260a2023-02-22 22:44:39 +0000300static const struct rockchip_efuse_data rk3399_data = {
301 .read = rockchip_rk3399_efuse_read,
302 .size = 0x80,
303 .block_size = 4,
304};
305
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200306static const struct udevice_id rockchip_efuse_ids[] = {
Jonas Karlman433260a2023-02-22 22:44:39 +0000307 {
Jonas Karlmandd5684b2023-02-22 22:44:40 +0000308 .compatible = "rockchip,rk3036-efuse",
309 .data = (ulong)&rk3036_data,
310 },
311 {
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000312 .compatible = "rockchip,rk3066a-efuse",
313 .data = (ulong)&rk3288_data,
314 },
315 {
Jonas Karlman16e8afa2023-02-22 22:44:40 +0000316 .compatible = "rockchip,rk3128-efuse",
317 .data = (ulong)&rk3128_data,
318 },
319 {
Jonas Karlman6f95b3e2023-02-22 22:44:40 +0000320 .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 {
Jonas Karlmanb0828cf2023-02-22 22:44:40 +0000332 .compatible = "rockchip,rk3328-efuse",
333 .data = (ulong)&rk3328_data,
334 },
335 {
Jonas Karlman433260a2023-02-22 22:44:39 +0000336 .compatible = "rockchip,rk3399-efuse",
337 .data = (ulong)&rk3399_data,
338 },
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200339 {}
340};
341
342U_BOOT_DRIVER(rockchip_efuse) = {
343 .name = "rockchip_efuse",
344 .id = UCLASS_MISC,
345 .of_match = rockchip_efuse_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700346 .of_to_plat = rockchip_efuse_of_to_plat,
Jonas Karlman433260a2023-02-22 22:44:39 +0000347 .plat_auto = sizeof(struct rockchip_efuse_plat),
Philipp Tomsich49cd8e82017-05-05 19:21:38 +0200348 .ops = &rockchip_efuse_ops,
349};