blob: 2123c31038fce15dd10090773883d2cd2fc59629 [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
Finley Xiao20d52a02019-09-25 17:57:49 +02006#include <asm/io.h>
Jonas Karlmanf6029a82023-02-22 22:44:39 +00007#include <command.h>
8#include <display_options.h>
Finley Xiao20d52a02019-09-25 17:57:49 +02009#include <dm.h>
10#include <linux/bitops.h>
11#include <linux/delay.h>
Jonas Karlman47be21c2023-02-22 22:44:38 +000012#include <linux/iopoll.h>
Jonas Karlmanda835812023-02-22 22:44:39 +000013#include <malloc.h>
Finley Xiao20d52a02019-09-25 17:57:49 +020014#include <misc.h>
15
16/* OTP Register Offsets */
17#define OTPC_SBPI_CTRL 0x0020
18#define OTPC_SBPI_CMD_VALID_PRE 0x0024
19#define OTPC_SBPI_CS_VALID_PRE 0x0028
20#define OTPC_SBPI_STATUS 0x002C
21#define OTPC_USER_CTRL 0x0100
22#define OTPC_USER_ADDR 0x0104
23#define OTPC_USER_ENABLE 0x0108
24#define OTPC_USER_QP 0x0120
25#define OTPC_USER_Q 0x0124
26#define OTPC_INT_STATUS 0x0304
27#define OTPC_SBPI_CMD0_OFFSET 0x1000
28#define OTPC_SBPI_CMD1_OFFSET 0x1004
29
30/* OTP Register bits and masks */
31#define OTPC_USER_ADDR_MASK GENMASK(31, 16)
32#define OTPC_USE_USER BIT(0)
33#define OTPC_USE_USER_MASK GENMASK(16, 16)
34#define OTPC_USER_FSM_ENABLE BIT(0)
35#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16)
36#define OTPC_SBPI_DONE BIT(1)
37#define OTPC_USER_DONE BIT(2)
38
39#define SBPI_DAP_ADDR 0x02
40#define SBPI_DAP_ADDR_SHIFT 8
41#define SBPI_DAP_ADDR_MASK GENMASK(31, 24)
42#define SBPI_CMD_VALID_MASK GENMASK(31, 16)
43#define SBPI_DAP_CMD_WRF 0xC0
44#define SBPI_DAP_REG_ECC 0x3A
45#define SBPI_ECC_ENABLE 0x00
46#define SBPI_ECC_DISABLE 0x09
47#define SBPI_ENABLE BIT(0)
48#define SBPI_ENABLE_MASK GENMASK(16, 16)
49
50#define OTPC_TIMEOUT 10000
51
Jonas Karlman9a6188a2023-02-22 22:44:39 +000052#define RK3588_OTPC_AUTO_CTRL 0x0004
53#define RK3588_ADDR_SHIFT 16
54#define RK3588_ADDR(n) ((n) << RK3588_ADDR_SHIFT)
55#define RK3588_BURST_SHIFT 8
56#define RK3588_BURST(n) ((n) << RK3588_BURST_SHIFT)
57#define RK3588_OTPC_AUTO_EN 0x0008
58#define RK3588_AUTO_EN BIT(0)
59#define RK3588_OTPC_DOUT0 0x0020
60#define RK3588_OTPC_INT_ST 0x0084
61#define RK3588_RD_DONE BIT(1)
62
Tim Lunnc9bc0622023-10-31 13:07:14 +110063#define RV1126_OTP_NVM_CEB 0x00
64#define RV1126_OTP_NVM_RSTB 0x04
65#define RV1126_OTP_NVM_ST 0x18
66#define RV1126_OTP_NVM_RADDR 0x1C
67#define RV1126_OTP_NVM_RSTART 0x20
68#define RV1126_OTP_NVM_RDATA 0x24
69#define RV1126_OTP_READ_ST 0x30
70
Simon Glassb75b15b2020-12-03 16:55:23 -070071struct rockchip_otp_plat {
Finley Xiao20d52a02019-09-25 17:57:49 +020072 void __iomem *base;
Finley Xiao20d52a02019-09-25 17:57:49 +020073};
74
Jonas Karlman47be21c2023-02-22 22:44:38 +000075struct rockchip_otp_data {
Tim Lunnc9bc0622023-10-31 13:07:14 +110076 int (*init)(struct udevice *dev);
Jonas Karlman47be21c2023-02-22 22:44:38 +000077 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlman9a6188a2023-02-22 22:44:39 +000078 int offset;
Jonas Karlman47be21c2023-02-22 22:44:38 +000079 int size;
Jonas Karlmanda835812023-02-22 22:44:39 +000080 int block_size;
Jonas Karlman47be21c2023-02-22 22:44:38 +000081};
82
Jonas Karlmanf6029a82023-02-22 22:44:39 +000083#if defined(DEBUG)
84static int dump_otp(struct cmd_tbl *cmdtp, int flag,
85 int argc, char *const argv[])
86{
87 struct udevice *dev;
88 u8 data[4];
89 int ret, i;
90
91 ret = uclass_get_device_by_driver(UCLASS_MISC,
92 DM_DRIVER_GET(rockchip_otp), &dev);
93 if (ret) {
94 printf("%s: no misc-device found\n", __func__);
95 return 0;
96 }
97
98 for (i = 0; true; i += sizeof(data)) {
99 ret = misc_read(dev, i, &data, sizeof(data));
John Keeping72d9d642023-03-27 12:01:10 +0100100 if (ret <= 0)
Jonas Karlmanf6029a82023-02-22 22:44:39 +0000101 return 0;
102
103 print_buffer(i, data, 1, sizeof(data), sizeof(data));
104 }
105
106 return 0;
107}
108
109U_BOOT_CMD(
110 dump_otp, 1, 1, dump_otp,
111 "Dump the content of the otp",
112 ""
113);
114#endif
115
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000116static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp,
117 u32 flag, u32 reg)
Finley Xiao20d52a02019-09-25 17:57:49 +0200118{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000119 u32 status;
120 int ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200121
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000122 ret = readl_poll_sleep_timeout(otp->base + reg, status,
Jonas Karlman47be21c2023-02-22 22:44:38 +0000123 (status & flag), 1, OTPC_TIMEOUT);
124 if (ret)
125 return ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200126
Jonas Karlman47be21c2023-02-22 22:44:38 +0000127 /* Clear int flag */
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000128 writel(flag, otp->base + reg);
Finley Xiao20d52a02019-09-25 17:57:49 +0200129
130 return 0;
131}
132
Jonas Karlman47be21c2023-02-22 22:44:38 +0000133static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable)
Finley Xiao20d52a02019-09-25 17:57:49 +0200134{
Finley Xiao20d52a02019-09-25 17:57:49 +0200135 writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
136 otp->base + OTPC_SBPI_CTRL);
137
138 writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
139 writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
140 otp->base + OTPC_SBPI_CMD0_OFFSET);
141
142 if (enable)
143 writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
144 else
145 writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
146
147 writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
148
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000149 return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE, OTPC_INT_STATUS);
Finley Xiao20d52a02019-09-25 17:57:49 +0200150}
151
152static int rockchip_px30_otp_read(struct udevice *dev, int offset,
153 void *buf, int size)
154{
Simon Glassb75b15b2020-12-03 16:55:23 -0700155 struct rockchip_otp_plat *otp = dev_get_plat(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200156 u8 *buffer = buf;
Jonas Karlman47be21c2023-02-22 22:44:38 +0000157 int ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200158
159 ret = rockchip_otp_ecc_enable(otp, false);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000160 if (ret)
Finley Xiao20d52a02019-09-25 17:57:49 +0200161 return ret;
Finley Xiao20d52a02019-09-25 17:57:49 +0200162
163 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
164 udelay(5);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000165
Finley Xiao20d52a02019-09-25 17:57:49 +0200166 while (size--) {
167 writel(offset++ | OTPC_USER_ADDR_MASK,
168 otp->base + OTPC_USER_ADDR);
169 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
170 otp->base + OTPC_USER_ENABLE);
171
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000172 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
173 OTPC_INT_STATUS);
Jonas Karlman47be21c2023-02-22 22:44:38 +0000174 if (ret)
Finley Xiao20d52a02019-09-25 17:57:49 +0200175 goto read_end;
Finley Xiao20d52a02019-09-25 17:57:49 +0200176
Jonas Karlman47be21c2023-02-22 22:44:38 +0000177 *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF);
Finley Xiao20d52a02019-09-25 17:57:49 +0200178 }
179
180read_end:
181 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
182
183 return ret;
184}
185
Jonas Karlmanda835812023-02-22 22:44:39 +0000186static int rockchip_rk3568_otp_read(struct udevice *dev, int offset,
187 void *buf, int size)
188{
189 struct rockchip_otp_plat *otp = dev_get_plat(dev);
190 u16 *buffer = buf;
191 int ret;
192
193 ret = rockchip_otp_ecc_enable(otp, false);
194 if (ret)
195 return ret;
196
197 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
198 udelay(5);
199
200 while (size--) {
201 writel(offset++ | OTPC_USER_ADDR_MASK,
202 otp->base + OTPC_USER_ADDR);
203 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
204 otp->base + OTPC_USER_ENABLE);
205
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000206 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
207 OTPC_INT_STATUS);
Jonas Karlmanda835812023-02-22 22:44:39 +0000208 if (ret)
209 goto read_end;
210
211 *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF);
212 }
213
214read_end:
215 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
216
217 return ret;
218}
219
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000220static int rockchip_rk3588_otp_read(struct udevice *dev, int offset,
221 void *buf, int size)
222{
223 struct rockchip_otp_plat *otp = dev_get_plat(dev);
224 u32 *buffer = buf;
225 int ret;
226
227 while (size--) {
228 writel(RK3588_ADDR(offset++) | RK3588_BURST(1),
229 otp->base + RK3588_OTPC_AUTO_CTRL);
230 writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
231
232 ret = rockchip_otp_poll_timeout(otp, RK3588_RD_DONE,
233 RK3588_OTPC_INT_ST);
234 if (ret)
235 return ret;
236
237 *buffer++ = readl(otp->base + RK3588_OTPC_DOUT0);
238 }
239
240 return 0;
241}
242
Tim Lunnc9bc0622023-10-31 13:07:14 +1100243static int rockchip_rv1126_otp_init(struct udevice *dev)
244{
245 struct rockchip_otp_plat *otp = dev_get_plat(dev);
246 int ret;
247
248 writel(0x0, otp->base + RV1126_OTP_NVM_CEB);
249 ret = rockchip_otp_poll_timeout(otp, 0x1, RV1126_OTP_NVM_ST);
250
251 if (ret)
252 return ret;
253
254 writel(0x1, otp->base + RV1126_OTP_NVM_RSTB);
255 ret = rockchip_otp_poll_timeout(otp, 0x4, RV1126_OTP_NVM_ST);
256
257 if (ret)
258 return ret;
259
260 return 0;
261}
262
263static int rockchip_rv1126_otp_read(struct udevice *dev, int offset, void *buf,
264 int size)
265{
266 struct rockchip_otp_plat *otp = dev_get_plat(dev);
267 u32 status = 0;
268 u8 *buffer = buf;
269 int ret = 0;
270
271 while (size--) {
272 writel(offset++, otp->base + RV1126_OTP_NVM_RADDR);
273 writel(0x1, otp->base + RV1126_OTP_NVM_RSTART);
274 ret = readl_poll_timeout(otp->base + RV1126_OTP_READ_ST,
275 status, !status, OTPC_TIMEOUT);
276 if (ret)
277 return ret;
278
279 *buffer++ = (u8)(readl(otp->base + RV1126_OTP_NVM_RDATA) & 0xFF);
280 }
281
282 return 0;
283}
284
Finley Xiao20d52a02019-09-25 17:57:49 +0200285static int rockchip_otp_read(struct udevice *dev, int offset,
286 void *buf, int size)
287{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000288 const struct rockchip_otp_data *data =
289 (void *)dev_get_driver_data(dev);
Jonas Karlmanda835812023-02-22 22:44:39 +0000290 u32 block_start, block_end, block_offset, blocks;
291 u8 *buffer;
292 int ret;
Jonas Karlman47be21c2023-02-22 22:44:38 +0000293
294 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
295 return -EINVAL;
296
297 if (!data->read)
298 return -ENOSYS;
299
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000300 offset += data->offset;
301
John Keeping72d9d642023-03-27 12:01:10 +0100302 if (data->block_size <= 1) {
303 ret = data->read(dev, offset, buf, size);
304 goto done;
305 }
Jonas Karlmanda835812023-02-22 22:44:39 +0000306
307 block_start = offset / data->block_size;
308 block_offset = offset % data->block_size;
309 block_end = DIV_ROUND_UP(offset + size, data->block_size);
310 blocks = block_end - block_start;
311
312 buffer = calloc(blocks, data->block_size);
313 if (!buffer)
314 return -ENOMEM;
315
316 ret = data->read(dev, block_start, buffer, blocks);
317 if (!ret)
318 memcpy(buf, buffer + block_offset, size);
319
320 free(buffer);
John Keeping72d9d642023-03-27 12:01:10 +0100321
322done:
323 return ret < 0 ? ret : size;
Finley Xiao20d52a02019-09-25 17:57:49 +0200324}
325
326static const struct misc_ops rockchip_otp_ops = {
327 .read = rockchip_otp_read,
328};
329
Simon Glassaad29ae2020-12-03 16:55:21 -0700330static int rockchip_otp_of_to_plat(struct udevice *dev)
Finley Xiao20d52a02019-09-25 17:57:49 +0200331{
Jonas Karlman47be21c2023-02-22 22:44:38 +0000332 struct rockchip_otp_plat *plat = dev_get_plat(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200333
Jonas Karlman47be21c2023-02-22 22:44:38 +0000334 plat->base = dev_read_addr_ptr(dev);
Finley Xiao20d52a02019-09-25 17:57:49 +0200335
336 return 0;
337}
338
Tim Lunnc9bc0622023-10-31 13:07:14 +1100339static int rockchip_otp_probe(struct udevice *dev)
340{
341 struct rockchip_otp_data *data;
342
343 data = (struct rockchip_otp_data *)dev_get_driver_data(dev);
344 if (!data)
345 return -EINVAL;
346
347 if (data->init)
348 return data->init(dev);
349
350 return 0;
351}
352
Jonas Karlman47be21c2023-02-22 22:44:38 +0000353static const struct rockchip_otp_data px30_data = {
354 .read = rockchip_px30_otp_read,
355 .size = 0x40,
356};
357
Jonas Karlmanda835812023-02-22 22:44:39 +0000358static const struct rockchip_otp_data rk3568_data = {
359 .read = rockchip_rk3568_otp_read,
360 .size = 0x80,
361 .block_size = 2,
362};
363
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000364static const struct rockchip_otp_data rk3588_data = {
365 .read = rockchip_rk3588_otp_read,
366 .offset = 0xC00,
367 .size = 0x400,
368 .block_size = 4,
369};
370
Tim Lunnc9bc0622023-10-31 13:07:14 +1100371static const struct rockchip_otp_data rv1126_data = {
372 .init = rockchip_rv1126_otp_init,
373 .read = rockchip_rv1126_otp_read,
374 .size = 0x40,
375};
376
Finley Xiao20d52a02019-09-25 17:57:49 +0200377static const struct udevice_id rockchip_otp_ids[] = {
378 {
379 .compatible = "rockchip,px30-otp",
Jonas Karlman47be21c2023-02-22 22:44:38 +0000380 .data = (ulong)&px30_data,
Finley Xiao20d52a02019-09-25 17:57:49 +0200381 },
382 {
383 .compatible = "rockchip,rk3308-otp",
Jonas Karlman47be21c2023-02-22 22:44:38 +0000384 .data = (ulong)&px30_data,
Finley Xiao20d52a02019-09-25 17:57:49 +0200385 },
Jonas Karlmanda835812023-02-22 22:44:39 +0000386 {
387 .compatible = "rockchip,rk3568-otp",
388 .data = (ulong)&rk3568_data,
389 },
Jonas Karlman9a6188a2023-02-22 22:44:39 +0000390 {
391 .compatible = "rockchip,rk3588-otp",
392 .data = (ulong)&rk3588_data,
393 },
Tim Lunnc9bc0622023-10-31 13:07:14 +1100394 {
395 .compatible = "rockchip,rv1126-otp",
396 .data = (ulong)&rv1126_data,
397 },
Finley Xiao20d52a02019-09-25 17:57:49 +0200398 {}
399};
400
401U_BOOT_DRIVER(rockchip_otp) = {
402 .name = "rockchip_otp",
403 .id = UCLASS_MISC,
404 .of_match = rockchip_otp_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700405 .of_to_plat = rockchip_otp_of_to_plat,
Jonas Karlman47be21c2023-02-22 22:44:38 +0000406 .plat_auto = sizeof(struct rockchip_otp_plat),
407 .ops = &rockchip_otp_ops,
Tim Lunnc9bc0622023-10-31 13:07:14 +1100408 .probe = rockchip_otp_probe,
Finley Xiao20d52a02019-09-25 17:57:49 +0200409};