blob: 4f757083a1b6d9e8152a470381022c2b2f31d4f3 [file] [log] [blame]
Finley Xiao20d52a02019-09-25 17:57:49 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4 */
5
Tom Riniabb9a042024-05-18 20:20:43 -06006#include <common.h>
Finley Xiao20d52a02019-09-25 17:57:49 +02007#include <asm/io.h>
Jonas Karlmanf6029a82023-02-22 22:44:39 +00008#include <command.h>
9#include <display_options.h>
Finley Xiao20d52a02019-09-25 17:57:49 +020010#include <dm.h>
11#include <linux/bitops.h>
12#include <linux/delay.h>
Jonas Karlman47be21c2023-02-22 22:44:38 +000013#include <linux/iopoll.h>
Jonas Karlmanda835812023-02-22 22:44:39 +000014#include <malloc.h>
Finley Xiao20d52a02019-09-25 17:57:49 +020015#include <misc.h>
16
17/* OTP Register Offsets */
18#define OTPC_SBPI_CTRL 0x0020
19#define OTPC_SBPI_CMD_VALID_PRE 0x0024
20#define OTPC_SBPI_CS_VALID_PRE 0x0028
21#define OTPC_SBPI_STATUS 0x002C
22#define OTPC_USER_CTRL 0x0100
23#define OTPC_USER_ADDR 0x0104
24#define OTPC_USER_ENABLE 0x0108
25#define OTPC_USER_QP 0x0120
26#define OTPC_USER_Q 0x0124
27#define OTPC_INT_STATUS 0x0304
28#define OTPC_SBPI_CMD0_OFFSET 0x1000
29#define OTPC_SBPI_CMD1_OFFSET 0x1004
30
31/* OTP Register bits and masks */
32#define OTPC_USER_ADDR_MASK GENMASK(31, 16)
33#define OTPC_USE_USER BIT(0)
34#define OTPC_USE_USER_MASK GENMASK(16, 16)
35#define OTPC_USER_FSM_ENABLE BIT(0)
36#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16)
37#define OTPC_SBPI_DONE BIT(1)
38#define OTPC_USER_DONE BIT(2)
39
40#define SBPI_DAP_ADDR 0x02
41#define SBPI_DAP_ADDR_SHIFT 8
42#define SBPI_DAP_ADDR_MASK GENMASK(31, 24)
43#define SBPI_CMD_VALID_MASK GENMASK(31, 16)
44#define SBPI_DAP_CMD_WRF 0xC0
45#define SBPI_DAP_REG_ECC 0x3A
46#define SBPI_ECC_ENABLE 0x00
47#define SBPI_ECC_DISABLE 0x09
48#define SBPI_ENABLE BIT(0)
49#define SBPI_ENABLE_MASK GENMASK(16, 16)
50
51#define OTPC_TIMEOUT 10000
52
Jonas Karlman9a6188a2023-02-22 22:44:39 +000053#define RK3588_OTPC_AUTO_CTRL 0x0004
54#define RK3588_ADDR_SHIFT 16
55#define RK3588_ADDR(n) ((n) << RK3588_ADDR_SHIFT)
56#define RK3588_BURST_SHIFT 8
57#define RK3588_BURST(n) ((n) << RK3588_BURST_SHIFT)
58#define RK3588_OTPC_AUTO_EN 0x0008
59#define RK3588_AUTO_EN BIT(0)
60#define RK3588_OTPC_DOUT0 0x0020
61#define RK3588_OTPC_INT_ST 0x0084
62#define RK3588_RD_DONE BIT(1)
63
Tim Lunnc9bc0622023-10-31 13:07:14 +110064#define RV1126_OTP_NVM_CEB 0x00
65#define RV1126_OTP_NVM_RSTB 0x04
66#define RV1126_OTP_NVM_ST 0x18
67#define RV1126_OTP_NVM_RADDR 0x1C
68#define RV1126_OTP_NVM_RSTART 0x20
69#define RV1126_OTP_NVM_RDATA 0x24
70#define RV1126_OTP_READ_ST 0x30
71
Simon Glassb75b15b2020-12-03 16:55:23 -070072struct rockchip_otp_plat {
Finley Xiao20d52a02019-09-25 17:57:49 +020073 void __iomem *base;
Finley Xiao20d52a02019-09-25 17:57:49 +020074};
75
Jonas Karlman47be21c2023-02-22 22:44:38 +000076struct rockchip_otp_data {
Tim Lunnc9bc0622023-10-31 13:07:14 +110077 int (*init)(struct udevice *dev);
Jonas Karlman47be21c2023-02-22 22:44:38 +000078 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlman9a6188a2023-02-22 22:44:39 +000079 int offset;
Jonas Karlman47be21c2023-02-22 22:44:38 +000080 int size;
Jonas Karlmanda835812023-02-22 22:44:39 +000081 int block_size;
Jonas Karlman47be21c2023-02-22 22:44:38 +000082};
83
Jonas Karlmanf6029a82023-02-22 22:44:39 +000084#if defined(DEBUG)
85static int dump_otp(struct cmd_tbl *cmdtp, int flag,
86 int argc, char *const argv[])
87{
88 struct udevice *dev;
89 u8 data[4];
90 int ret, i;
91
92 ret = uclass_get_device_by_driver(UCLASS_MISC,
93 DM_DRIVER_GET(rockchip_otp), &dev);
94 if (ret) {
95 printf("%s: no misc-device found\n", __func__);
96 return 0;
97 }
98
99 for (i = 0; true; i += sizeof(data)) {
100 ret = misc_read(dev, i, &data, sizeof(data));
John Keeping72d9d642023-03-27 12:01:10 +0100101 if (ret <= 0)
Jonas Karlmanf6029a82023-02-22 22:44:39 +0000102 return 0;
103
104 print_buffer(i, data, 1, sizeof(data), sizeof(data));
105 }
106
107 return 0;
108}
109
110U_BOOT_CMD(
111 dump_otp, 1, 1, dump_otp,
112 "Dump the content of the otp",
113 ""
114);
115#endif
116
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000117static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp,
118 u32 flag, u32 reg)
Finley Xiao20d52a02019-09-25 17:57:49 +0200119{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000120 u32 status;
121 int ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200122
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000123 ret = readl_poll_sleep_timeout(otp->base + reg, status,
Jonas Karlman47be21c2023-02-22 22:44:38 +0000124 (status & flag), 1, OTPC_TIMEOUT);
125 if (ret)
126 return ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200127
Jonas Karlman47be21c2023-02-22 22:44:38 +0000128 /* Clear int flag */
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000129 writel(flag, otp->base + reg);
Finley Xiao20d52a02019-09-25 17:57:49 +0200130
131 return 0;
132}
133
Jonas Karlman47be21c2023-02-22 22:44:38 +0000134static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable)
Finley Xiao20d52a02019-09-25 17:57:49 +0200135{
Finley Xiao20d52a02019-09-25 17:57:49 +0200136 writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
137 otp->base + OTPC_SBPI_CTRL);
138
139 writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
140 writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
141 otp->base + OTPC_SBPI_CMD0_OFFSET);
142
143 if (enable)
144 writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
145 else
146 writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
147
148 writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
149
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000150 return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE, OTPC_INT_STATUS);
Finley Xiao20d52a02019-09-25 17:57:49 +0200151}
152
153static int rockchip_px30_otp_read(struct udevice *dev, int offset,
154 void *buf, int size)
155{
Simon Glassb75b15b2020-12-03 16:55:23 -0700156 struct rockchip_otp_plat *otp = dev_get_plat(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200157 u8 *buffer = buf;
Jonas Karlman47be21c2023-02-22 22:44:38 +0000158 int ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200159
160 ret = rockchip_otp_ecc_enable(otp, false);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000161 if (ret)
Finley Xiao20d52a02019-09-25 17:57:49 +0200162 return ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200163
164 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
165 udelay(5);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000166
Finley Xiao20d52a02019-09-25 17:57:49 +0200167 while (size--) {
168 writel(offset++ | OTPC_USER_ADDR_MASK,
169 otp->base + OTPC_USER_ADDR);
170 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
171 otp->base + OTPC_USER_ENABLE);
172
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000173 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
174 OTPC_INT_STATUS);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000175 if (ret)
Finley Xiao20d52a02019-09-25 17:57:49 +0200176 goto read_end;
Finley Xiao20d52a02019-09-25 17:57:49 +0200177
Jonas Karlman47be21c2023-02-22 22:44:38 +0000178 *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF);
Finley Xiao20d52a02019-09-25 17:57:49 +0200179 }
180
181read_end:
182 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
183
184 return ret;
185}
186
Jonas Karlmanda835812023-02-22 22:44:39 +0000187static int rockchip_rk3568_otp_read(struct udevice *dev, int offset,
188 void *buf, int size)
189{
190 struct rockchip_otp_plat *otp = dev_get_plat(dev);
191 u16 *buffer = buf;
192 int ret;
193
194 ret = rockchip_otp_ecc_enable(otp, false);
195 if (ret)
196 return ret;
197
198 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
199 udelay(5);
200
201 while (size--) {
202 writel(offset++ | OTPC_USER_ADDR_MASK,
203 otp->base + OTPC_USER_ADDR);
204 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
205 otp->base + OTPC_USER_ENABLE);
206
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000207 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
208 OTPC_INT_STATUS);
Jonas Karlmanda835812023-02-22 22:44:39 +0000209 if (ret)
210 goto read_end;
211
212 *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF);
213 }
214
215read_end:
216 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
217
218 return ret;
219}
220
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000221static int rockchip_rk3588_otp_read(struct udevice *dev, int offset,
222 void *buf, int size)
223{
224 struct rockchip_otp_plat *otp = dev_get_plat(dev);
225 u32 *buffer = buf;
226 int ret;
227
228 while (size--) {
229 writel(RK3588_ADDR(offset++) | RK3588_BURST(1),
230 otp->base + RK3588_OTPC_AUTO_CTRL);
231 writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
232
233 ret = rockchip_otp_poll_timeout(otp, RK3588_RD_DONE,
234 RK3588_OTPC_INT_ST);
235 if (ret)
236 return ret;
237
238 *buffer++ = readl(otp->base + RK3588_OTPC_DOUT0);
239 }
240
241 return 0;
242}
243
Tim Lunnc9bc0622023-10-31 13:07:14 +1100244static int rockchip_rv1126_otp_init(struct udevice *dev)
245{
246 struct rockchip_otp_plat *otp = dev_get_plat(dev);
247 int ret;
248
249 writel(0x0, otp->base + RV1126_OTP_NVM_CEB);
250 ret = rockchip_otp_poll_timeout(otp, 0x1, RV1126_OTP_NVM_ST);
251
252 if (ret)
253 return ret;
254
255 writel(0x1, otp->base + RV1126_OTP_NVM_RSTB);
256 ret = rockchip_otp_poll_timeout(otp, 0x4, RV1126_OTP_NVM_ST);
257
258 if (ret)
259 return ret;
260
261 return 0;
262}
263
264static int rockchip_rv1126_otp_read(struct udevice *dev, int offset, void *buf,
265 int size)
266{
267 struct rockchip_otp_plat *otp = dev_get_plat(dev);
268 u32 status = 0;
269 u8 *buffer = buf;
270 int ret = 0;
271
272 while (size--) {
273 writel(offset++, otp->base + RV1126_OTP_NVM_RADDR);
274 writel(0x1, otp->base + RV1126_OTP_NVM_RSTART);
275 ret = readl_poll_timeout(otp->base + RV1126_OTP_READ_ST,
276 status, !status, OTPC_TIMEOUT);
277 if (ret)
278 return ret;
279
280 *buffer++ = (u8)(readl(otp->base + RV1126_OTP_NVM_RDATA) & 0xFF);
281 }
282
283 return 0;
284}
285
Finley Xiao20d52a02019-09-25 17:57:49 +0200286static int rockchip_otp_read(struct udevice *dev, int offset,
287 void *buf, int size)
288{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000289 const struct rockchip_otp_data *data =
290 (void *)dev_get_driver_data(dev);
Jonas Karlmanda835812023-02-22 22:44:39 +0000291 u32 block_start, block_end, block_offset, blocks;
292 u8 *buffer;
293 int ret;
Jonas Karlman47be21c2023-02-22 22:44:38 +0000294
295 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
296 return -EINVAL;
297
298 if (!data->read)
299 return -ENOSYS;
300
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000301 offset += data->offset;
302
John Keeping72d9d642023-03-27 12:01:10 +0100303 if (data->block_size <= 1) {
304 ret = data->read(dev, offset, buf, size);
305 goto done;
306 }
Jonas Karlmanda835812023-02-22 22:44:39 +0000307
308 block_start = offset / data->block_size;
309 block_offset = offset % data->block_size;
310 block_end = DIV_ROUND_UP(offset + size, data->block_size);
311 blocks = block_end - block_start;
312
313 buffer = calloc(blocks, data->block_size);
314 if (!buffer)
315 return -ENOMEM;
316
317 ret = data->read(dev, block_start, buffer, blocks);
318 if (!ret)
319 memcpy(buf, buffer + block_offset, size);
320
321 free(buffer);
John Keeping72d9d642023-03-27 12:01:10 +0100322
323done:
324 return ret < 0 ? ret : size;
Finley Xiao20d52a02019-09-25 17:57:49 +0200325}
326
327static const struct misc_ops rockchip_otp_ops = {
328 .read = rockchip_otp_read,
329};
330
Simon Glassaad29ae2020-12-03 16:55:21 -0700331static int rockchip_otp_of_to_plat(struct udevice *dev)
Finley Xiao20d52a02019-09-25 17:57:49 +0200332{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000333 struct rockchip_otp_plat *plat = dev_get_plat(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200334
Jonas Karlman47be21c2023-02-22 22:44:38 +0000335 plat->base = dev_read_addr_ptr(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200336
337 return 0;
338}
339
Tim Lunnc9bc0622023-10-31 13:07:14 +1100340static int rockchip_otp_probe(struct udevice *dev)
341{
342 struct rockchip_otp_data *data;
343
344 data = (struct rockchip_otp_data *)dev_get_driver_data(dev);
345 if (!data)
346 return -EINVAL;
347
348 if (data->init)
349 return data->init(dev);
350
351 return 0;
352}
353
Jonas Karlman47be21c2023-02-22 22:44:38 +0000354static const struct rockchip_otp_data px30_data = {
355 .read = rockchip_px30_otp_read,
356 .size = 0x40,
357};
358
Jonas Karlmanda835812023-02-22 22:44:39 +0000359static const struct rockchip_otp_data rk3568_data = {
360 .read = rockchip_rk3568_otp_read,
361 .size = 0x80,
362 .block_size = 2,
363};
364
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000365static const struct rockchip_otp_data rk3588_data = {
366 .read = rockchip_rk3588_otp_read,
367 .offset = 0xC00,
368 .size = 0x400,
369 .block_size = 4,
370};
371
Tim Lunnc9bc0622023-10-31 13:07:14 +1100372static const struct rockchip_otp_data rv1126_data = {
373 .init = rockchip_rv1126_otp_init,
374 .read = rockchip_rv1126_otp_read,
375 .size = 0x40,
376};
377
Finley Xiao20d52a02019-09-25 17:57:49 +0200378static const struct udevice_id rockchip_otp_ids[] = {
379 {
380 .compatible = "rockchip,px30-otp",
Jonas Karlman47be21c2023-02-22 22:44:38 +0000381 .data = (ulong)&px30_data,
Finley Xiao20d52a02019-09-25 17:57:49 +0200382 },
383 {
384 .compatible = "rockchip,rk3308-otp",
Jonas Karlman47be21c2023-02-22 22:44:38 +0000385 .data = (ulong)&px30_data,
Finley Xiao20d52a02019-09-25 17:57:49 +0200386 },
Jonas Karlmanda835812023-02-22 22:44:39 +0000387 {
388 .compatible = "rockchip,rk3568-otp",
389 .data = (ulong)&rk3568_data,
390 },
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000391 {
392 .compatible = "rockchip,rk3588-otp",
393 .data = (ulong)&rk3588_data,
394 },
Tim Lunnc9bc0622023-10-31 13:07:14 +1100395 {
396 .compatible = "rockchip,rv1126-otp",
397 .data = (ulong)&rv1126_data,
398 },
Finley Xiao20d52a02019-09-25 17:57:49 +0200399 {}
400};
401
402U_BOOT_DRIVER(rockchip_otp) = {
403 .name = "rockchip_otp",
404 .id = UCLASS_MISC,
405 .of_match = rockchip_otp_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700406 .of_to_plat = rockchip_otp_of_to_plat,
Jonas Karlman47be21c2023-02-22 22:44:38 +0000407 .plat_auto = sizeof(struct rockchip_otp_plat),
408 .ops = &rockchip_otp_ops,
Tim Lunnc9bc0622023-10-31 13:07:14 +1100409 .probe = rockchip_otp_probe,
Finley Xiao20d52a02019-09-25 17:57:49 +0200410};