blob: c7430147718a88c32a54424fe720b228e9aeeeda [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Philipp Tomsichfcc1d632017-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 Tomsichfcc1d632017-05-05 19:21:38 +02007 */
8
Philipp Tomsichfcc1d632017-05-05 19:21:38 +02009#include <asm/io.h>
10#include <command.h>
11#include <display_options.h>
12#include <dm.h>
13#include <linux/bitops.h>
14#include <linux/delay.h>
Jonas Karlmanc0189652023-02-22 22:44:40 +000015#include <linux/iopoll.h>
Jonas Karlman26ed3852023-02-22 22:44:39 +000016#include <malloc.h>
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020017#include <misc.h>
18
Jonas Karlman26ed3852023-02-22 22:44:39 +000019#define EFUSE_CTRL 0x0000
Jonas Karlmandececbb2023-02-22 22:44:40 +000020#define RK3036_A_SHIFT 8
21#define RK3036_A_MASK GENMASK(15, 8)
22#define RK3036_ADDR(n) ((n) << RK3036_A_SHIFT)
Jonas Karlman836f4ec2023-02-22 22:44:40 +000023#define RK3128_A_SHIFT 7
24#define RK3128_A_MASK GENMASK(15, 7)
25#define RK3128_ADDR(n) ((n) << RK3128_A_SHIFT)
Jonas Karlman14ad36d2023-02-22 22:44:40 +000026#define RK3288_A_SHIFT 6
27#define RK3288_A_MASK GENMASK(15, 6)
28#define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT)
Jonas Karlman26ed3852023-02-22 22:44:39 +000029#define RK3399_A_SHIFT 16
30#define RK3399_A_MASK GENMASK(25, 16)
31#define RK3399_ADDR(n) ((n) << RK3399_A_SHIFT)
32#define RK3399_STROBSFTSEL BIT(9)
33#define RK3399_RSB BIT(7)
34#define RK3399_PD BIT(5)
Jonas Karlman14ad36d2023-02-22 22:44:40 +000035#define EFUSE_PGENB BIT(3)
36#define EFUSE_LOAD BIT(2)
37#define EFUSE_STROBE BIT(1)
38#define EFUSE_CSB BIT(0)
Jonas Karlman26ed3852023-02-22 22:44:39 +000039#define EFUSE_DOUT 0x0004
Jonas Karlmanc0189652023-02-22 22:44:40 +000040#define RK3328_INT_STATUS 0x0018
41#define RK3328_INT_FINISH BIT(0)
42#define RK3328_DOUT 0x0020
43#define RK3328_AUTO_CTRL 0x0024
44#define RK3328_AUTO_RD BIT(1)
45#define RK3328_AUTO_ENB BIT(0)
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020046
Simon Glassb75b15b2020-12-03 16:55:23 -070047struct rockchip_efuse_plat {
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020048 void __iomem *base;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020049};
50
Jonas Karlman26ed3852023-02-22 22:44:39 +000051struct rockchip_efuse_data {
52 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlmanc0189652023-02-22 22:44:40 +000053 int offset;
Jonas Karlman26ed3852023-02-22 22:44:39 +000054 int size;
55 int block_size;
56};
57
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020058#if defined(DEBUG)
Jonas Karlman26ed3852023-02-22 22:44:39 +000059static int dump_efuse(struct cmd_tbl *cmdtp, int flag,
60 int argc, char *const argv[])
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020061{
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020062 struct udevice *dev;
Jonas Karlman26ed3852023-02-22 22:44:39 +000063 u8 data[4];
64 int ret, i;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020065
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020066 ret = uclass_get_device_by_driver(UCLASS_MISC,
Simon Glass65130cd2020-12-28 20:34:56 -070067 DM_DRIVER_GET(rockchip_efuse), &dev);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020068 if (ret) {
69 printf("%s: no misc-device found\n", __func__);
70 return 0;
71 }
72
Jonas Karlman26ed3852023-02-22 22:44:39 +000073 for (i = 0; true; i += sizeof(data)) {
74 ret = misc_read(dev, i, &data, sizeof(data));
John Keepingdb635282023-03-27 12:01:09 +010075 if (ret <= 0)
Jonas Karlman26ed3852023-02-22 22:44:39 +000076 return 0;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020077
Jonas Karlman26ed3852023-02-22 22:44:39 +000078 print_buffer(i, data, 1, sizeof(data), sizeof(data));
79 }
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020080
81 return 0;
82}
83
84U_BOOT_CMD(
Jonas Karlman26ed3852023-02-22 22:44:39 +000085 dump_efuse, 1, 1, dump_efuse,
86 "Dump the content of the efuse",
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020087 ""
88);
89#endif
90
Jonas Karlmandececbb2023-02-22 22:44:40 +000091static int rockchip_rk3036_efuse_read(struct udevice *dev, int offset,
92 void *buf, int size)
93{
94 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
95 u8 *buffer = buf;
96
97 /* Switch to read mode */
98 writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL);
99 udelay(2);
100
101 while (size--) {
102 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3036_A_MASK,
103 RK3036_ADDR(offset++));
104 udelay(2);
105 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
106 udelay(2);
107 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
108 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
109 udelay(2);
110 }
111
112 /* Switch to inactive mode */
113 writel(0x0, efuse->base + EFUSE_CTRL);
114
115 return 0;
116}
117
Jonas Karlman836f4ec2023-02-22 22:44:40 +0000118static int rockchip_rk3128_efuse_read(struct udevice *dev, int offset,
119 void *buf, int size)
120{
121 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
122 u8 *buffer = buf;
123
124 /* Switch to read mode */
125 writel(EFUSE_LOAD, efuse->base + EFUSE_CTRL);
126 udelay(2);
127
128 while (size--) {
129 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3128_A_MASK,
130 RK3128_ADDR(offset++));
131 udelay(2);
132 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
133 udelay(2);
134 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
135 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
136 udelay(2);
137 }
138
139 /* Switch to inactive mode */
140 writel(0x0, efuse->base + EFUSE_CTRL);
141
142 return 0;
143}
144
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000145static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
146 void *buf, int size)
147{
148 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
149 u8 *buffer = buf;
150
151 /* Switch to read mode */
152 writel(EFUSE_CSB, efuse->base + EFUSE_CTRL);
153 writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
154 udelay(2);
155
156 while (size--) {
157 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3288_A_MASK,
158 RK3288_ADDR(offset++));
159 udelay(2);
160 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
161 udelay(2);
162 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
163 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
164 udelay(2);
165 }
166
167 /* Switch to standby mode */
168 writel(EFUSE_CSB | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
169
170 return 0;
171}
172
Jonas Karlmanc0189652023-02-22 22:44:40 +0000173static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
174 void *buf, int size)
175{
176 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
177 u32 status, *buffer = buf;
178 int ret;
179
180 while (size--) {
181 writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | RK3399_ADDR(offset++),
182 efuse->base + RK3328_AUTO_CTRL);
183 udelay(1);
184
185 ret = readl_poll_sleep_timeout(efuse->base + RK3328_INT_STATUS,
186 status, (status & RK3328_INT_FINISH), 1, 50);
187 if (ret)
188 return ret;
189
190 *buffer++ = readl(efuse->base + RK3328_DOUT);
191 writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS);
192 }
193
194 return 0;
195}
196
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200197static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
198 void *buf, int size)
199{
Jonas Karlman26ed3852023-02-22 22:44:39 +0000200 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
201 u32 *buffer = buf;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200202
Jonas Karlman26ed3852023-02-22 22:44:39 +0000203 /* Switch to array read mode */
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000204 writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
Jonas Karlman26ed3852023-02-22 22:44:39 +0000205 efuse->base + EFUSE_CTRL);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200206 udelay(1);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000207
208 while (size--) {
209 setbits_le32(efuse->base + EFUSE_CTRL,
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000210 EFUSE_STROBE | RK3399_ADDR(offset++));
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200211 udelay(1);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000212 *buffer++ = readl(efuse->base + EFUSE_DOUT);
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000213 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200214 udelay(1);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200215 }
216
Jonas Karlman26ed3852023-02-22 22:44:39 +0000217 /* Switch to power-down mode */
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000218 writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200219
220 return 0;
221}
222
223static int rockchip_efuse_read(struct udevice *dev, int offset,
224 void *buf, int size)
225{
Jonas Karlman26ed3852023-02-22 22:44:39 +0000226 const struct rockchip_efuse_data *data =
227 (void *)dev_get_driver_data(dev);
228 u32 block_start, block_end, block_offset, blocks;
229 u8 *buffer;
230 int ret;
231
232 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
233 return -EINVAL;
234
235 if (!data->read)
236 return -ENOSYS;
237
Jonas Karlmanc0189652023-02-22 22:44:40 +0000238 offset += data->offset;
239
John Keepingdb635282023-03-27 12:01:09 +0100240 if (data->block_size <= 1) {
241 ret = data->read(dev, offset, buf, size);
242 goto done;
243 }
Jonas Karlman26ed3852023-02-22 22:44:39 +0000244
245 block_start = offset / data->block_size;
246 block_offset = offset % data->block_size;
247 block_end = DIV_ROUND_UP(offset + size, data->block_size);
248 blocks = block_end - block_start;
249
250 buffer = calloc(blocks, data->block_size);
251 if (!buffer)
252 return -ENOMEM;
253
254 ret = data->read(dev, block_start, buffer, blocks);
255 if (!ret)
256 memcpy(buf, buffer + block_offset, size);
257
258 free(buffer);
John Keepingdb635282023-03-27 12:01:09 +0100259
260done:
261 return ret < 0 ? ret : size;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200262}
263
264static const struct misc_ops rockchip_efuse_ops = {
265 .read = rockchip_efuse_read,
266};
267
Simon Glassaad29ae2020-12-03 16:55:21 -0700268static int rockchip_efuse_of_to_plat(struct udevice *dev)
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200269{
Simon Glassb75b15b2020-12-03 16:55:23 -0700270 struct rockchip_efuse_plat *plat = dev_get_plat(dev);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200271
Philipp Tomsich3b505962017-09-12 17:32:26 +0200272 plat->base = dev_read_addr_ptr(dev);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000273
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200274 return 0;
275}
276
Jonas Karlmandececbb2023-02-22 22:44:40 +0000277static const struct rockchip_efuse_data rk3036_data = {
278 .read = rockchip_rk3036_efuse_read,
279 .size = 0x20,
280};
281
Jonas Karlman836f4ec2023-02-22 22:44:40 +0000282static const struct rockchip_efuse_data rk3128_data = {
283 .read = rockchip_rk3128_efuse_read,
284 .size = 0x40,
285};
286
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000287static const struct rockchip_efuse_data rk3288_data = {
288 .read = rockchip_rk3288_efuse_read,
289 .size = 0x20,
Jonas Karlmanc0189652023-02-22 22:44:40 +0000290};
291
292static const struct rockchip_efuse_data rk3328_data = {
293 .read = rockchip_rk3328_efuse_read,
294 .offset = 0x60,
295 .size = 0x20,
296 .block_size = 4,
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000297};
298
Jonas Karlman26ed3852023-02-22 22:44:39 +0000299static const struct rockchip_efuse_data rk3399_data = {
300 .read = rockchip_rk3399_efuse_read,
301 .size = 0x80,
302 .block_size = 4,
303};
304
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200305static const struct udevice_id rockchip_efuse_ids[] = {
Jonas Karlman26ed3852023-02-22 22:44:39 +0000306 {
Jonas Karlmandececbb2023-02-22 22:44:40 +0000307 .compatible = "rockchip,rk3036-efuse",
308 .data = (ulong)&rk3036_data,
309 },
310 {
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000311 .compatible = "rockchip,rk3066a-efuse",
312 .data = (ulong)&rk3288_data,
313 },
314 {
Jonas Karlman836f4ec2023-02-22 22:44:40 +0000315 .compatible = "rockchip,rk3128-efuse",
316 .data = (ulong)&rk3128_data,
317 },
318 {
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000319 .compatible = "rockchip,rk3188-efuse",
320 .data = (ulong)&rk3288_data,
321 },
322 {
323 .compatible = "rockchip,rk3228-efuse",
324 .data = (ulong)&rk3288_data,
325 },
326 {
327 .compatible = "rockchip,rk3288-efuse",
328 .data = (ulong)&rk3288_data,
329 },
330 {
Jonas Karlmanc0189652023-02-22 22:44:40 +0000331 .compatible = "rockchip,rk3328-efuse",
332 .data = (ulong)&rk3328_data,
333 },
334 {
Jonas Karlman26ed3852023-02-22 22:44:39 +0000335 .compatible = "rockchip,rk3399-efuse",
336 .data = (ulong)&rk3399_data,
337 },
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200338 {}
339};
340
341U_BOOT_DRIVER(rockchip_efuse) = {
342 .name = "rockchip_efuse",
343 .id = UCLASS_MISC,
344 .of_match = rockchip_efuse_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700345 .of_to_plat = rockchip_efuse_of_to_plat,
Jonas Karlman26ed3852023-02-22 22:44:39 +0000346 .plat_auto = sizeof(struct rockchip_efuse_plat),
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200347 .ops = &rockchip_efuse_ops,
348};