blob: 4814e0e501c9fa077a9bf4d9aeb8b29c5c659f2e [file] [log] [blame]
Finley Xiaoa907dc32019-09-25 17:57:49 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4 */
5
6#include <common.h>
7#include <asm/io.h>
Jonas Karlman9a850d12023-02-22 22:44:39 +00008#include <command.h>
9#include <display_options.h>
Finley Xiaoa907dc32019-09-25 17:57:49 +020010#include <dm.h>
11#include <linux/bitops.h>
12#include <linux/delay.h>
Jonas Karlman8fa18702023-02-22 22:44:38 +000013#include <linux/iopoll.h>
Jonas Karlmand58d55d2023-02-22 22:44:39 +000014#include <malloc.h>
Finley Xiaoa907dc32019-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 Karlmanf8880982023-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
Simon Glass8a8d24b2020-12-03 16:55:23 -070064struct rockchip_otp_plat {
Finley Xiaoa907dc32019-09-25 17:57:49 +020065 void __iomem *base;
Finley Xiaoa907dc32019-09-25 17:57:49 +020066};
67
Jonas Karlman8fa18702023-02-22 22:44:38 +000068struct rockchip_otp_data {
69 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlmanf8880982023-02-22 22:44:39 +000070 int offset;
Jonas Karlman8fa18702023-02-22 22:44:38 +000071 int size;
Jonas Karlmand58d55d2023-02-22 22:44:39 +000072 int block_size;
Jonas Karlman8fa18702023-02-22 22:44:38 +000073};
74
Jonas Karlman9a850d12023-02-22 22:44:39 +000075#if defined(DEBUG)
76static int dump_otp(struct cmd_tbl *cmdtp, int flag,
77 int argc, char *const argv[])
78{
79 struct udevice *dev;
80 u8 data[4];
81 int ret, i;
82
83 ret = uclass_get_device_by_driver(UCLASS_MISC,
84 DM_DRIVER_GET(rockchip_otp), &dev);
85 if (ret) {
86 printf("%s: no misc-device found\n", __func__);
87 return 0;
88 }
89
90 for (i = 0; true; i += sizeof(data)) {
91 ret = misc_read(dev, i, &data, sizeof(data));
John Keeping9fea3692023-03-27 12:01:10 +010092 if (ret <= 0)
Jonas Karlman9a850d12023-02-22 22:44:39 +000093 return 0;
94
95 print_buffer(i, data, 1, sizeof(data), sizeof(data));
96 }
97
98 return 0;
99}
100
101U_BOOT_CMD(
102 dump_otp, 1, 1, dump_otp,
103 "Dump the content of the otp",
104 ""
105);
106#endif
107
Jonas Karlmanf8880982023-02-22 22:44:39 +0000108static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp,
109 u32 flag, u32 reg)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200110{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000111 u32 status;
112 int ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200113
Jonas Karlmanf8880982023-02-22 22:44:39 +0000114 ret = readl_poll_sleep_timeout(otp->base + reg, status,
Jonas Karlman8fa18702023-02-22 22:44:38 +0000115 (status & flag), 1, OTPC_TIMEOUT);
116 if (ret)
117 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200118
Jonas Karlman8fa18702023-02-22 22:44:38 +0000119 /* Clear int flag */
Jonas Karlmanf8880982023-02-22 22:44:39 +0000120 writel(flag, otp->base + reg);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200121
122 return 0;
123}
124
Jonas Karlman8fa18702023-02-22 22:44:38 +0000125static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200126{
Finley Xiaoa907dc32019-09-25 17:57:49 +0200127 writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
128 otp->base + OTPC_SBPI_CTRL);
129
130 writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
131 writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
132 otp->base + OTPC_SBPI_CMD0_OFFSET);
133
134 if (enable)
135 writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
136 else
137 writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
138
139 writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
140
Jonas Karlmanf8880982023-02-22 22:44:39 +0000141 return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE, OTPC_INT_STATUS);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200142}
143
144static int rockchip_px30_otp_read(struct udevice *dev, int offset,
145 void *buf, int size)
146{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700147 struct rockchip_otp_plat *otp = dev_get_plat(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200148 u8 *buffer = buf;
Jonas Karlman8fa18702023-02-22 22:44:38 +0000149 int ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200150
151 ret = rockchip_otp_ecc_enable(otp, false);
Jonas Karlman8fa18702023-02-22 22:44:38 +0000152 if (ret)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200153 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200154
155 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
156 udelay(5);
Jonas Karlman8fa18702023-02-22 22:44:38 +0000157
Finley Xiaoa907dc32019-09-25 17:57:49 +0200158 while (size--) {
159 writel(offset++ | OTPC_USER_ADDR_MASK,
160 otp->base + OTPC_USER_ADDR);
161 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
162 otp->base + OTPC_USER_ENABLE);
163
Jonas Karlmanf8880982023-02-22 22:44:39 +0000164 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
165 OTPC_INT_STATUS);
Jonas Karlman8fa18702023-02-22 22:44:38 +0000166 if (ret)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200167 goto read_end;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200168
Jonas Karlman8fa18702023-02-22 22:44:38 +0000169 *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200170 }
171
172read_end:
173 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
174
175 return ret;
176}
177
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000178static int rockchip_rk3568_otp_read(struct udevice *dev, int offset,
179 void *buf, int size)
180{
181 struct rockchip_otp_plat *otp = dev_get_plat(dev);
182 u16 *buffer = buf;
183 int ret;
184
185 ret = rockchip_otp_ecc_enable(otp, false);
186 if (ret)
187 return ret;
188
189 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
190 udelay(5);
191
192 while (size--) {
193 writel(offset++ | OTPC_USER_ADDR_MASK,
194 otp->base + OTPC_USER_ADDR);
195 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
196 otp->base + OTPC_USER_ENABLE);
197
Jonas Karlmanf8880982023-02-22 22:44:39 +0000198 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
199 OTPC_INT_STATUS);
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000200 if (ret)
201 goto read_end;
202
203 *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF);
204 }
205
206read_end:
207 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
208
209 return ret;
210}
211
Jonas Karlmanf8880982023-02-22 22:44:39 +0000212static int rockchip_rk3588_otp_read(struct udevice *dev, int offset,
213 void *buf, int size)
214{
215 struct rockchip_otp_plat *otp = dev_get_plat(dev);
216 u32 *buffer = buf;
217 int ret;
218
219 while (size--) {
220 writel(RK3588_ADDR(offset++) | RK3588_BURST(1),
221 otp->base + RK3588_OTPC_AUTO_CTRL);
222 writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
223
224 ret = rockchip_otp_poll_timeout(otp, RK3588_RD_DONE,
225 RK3588_OTPC_INT_ST);
226 if (ret)
227 return ret;
228
229 *buffer++ = readl(otp->base + RK3588_OTPC_DOUT0);
230 }
231
232 return 0;
233}
234
Finley Xiaoa907dc32019-09-25 17:57:49 +0200235static int rockchip_otp_read(struct udevice *dev, int offset,
236 void *buf, int size)
237{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000238 const struct rockchip_otp_data *data =
239 (void *)dev_get_driver_data(dev);
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000240 u32 block_start, block_end, block_offset, blocks;
241 u8 *buffer;
242 int ret;
Jonas Karlman8fa18702023-02-22 22:44:38 +0000243
244 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
245 return -EINVAL;
246
247 if (!data->read)
248 return -ENOSYS;
249
Jonas Karlmanf8880982023-02-22 22:44:39 +0000250 offset += data->offset;
251
John Keeping9fea3692023-03-27 12:01:10 +0100252 if (data->block_size <= 1) {
253 ret = data->read(dev, offset, buf, size);
254 goto done;
255 }
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000256
257 block_start = offset / data->block_size;
258 block_offset = offset % data->block_size;
259 block_end = DIV_ROUND_UP(offset + size, data->block_size);
260 blocks = block_end - block_start;
261
262 buffer = calloc(blocks, data->block_size);
263 if (!buffer)
264 return -ENOMEM;
265
266 ret = data->read(dev, block_start, buffer, blocks);
267 if (!ret)
268 memcpy(buf, buffer + block_offset, size);
269
270 free(buffer);
John Keeping9fea3692023-03-27 12:01:10 +0100271
272done:
273 return ret < 0 ? ret : size;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200274}
275
276static const struct misc_ops rockchip_otp_ops = {
277 .read = rockchip_otp_read,
278};
279
Simon Glassd1998a92020-12-03 16:55:21 -0700280static int rockchip_otp_of_to_plat(struct udevice *dev)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200281{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000282 struct rockchip_otp_plat *plat = dev_get_plat(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200283
Jonas Karlman8fa18702023-02-22 22:44:38 +0000284 plat->base = dev_read_addr_ptr(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200285
286 return 0;
287}
288
Jonas Karlman8fa18702023-02-22 22:44:38 +0000289static const struct rockchip_otp_data px30_data = {
290 .read = rockchip_px30_otp_read,
291 .size = 0x40,
292};
293
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000294static const struct rockchip_otp_data rk3568_data = {
295 .read = rockchip_rk3568_otp_read,
296 .size = 0x80,
297 .block_size = 2,
298};
299
Jonas Karlmanf8880982023-02-22 22:44:39 +0000300static const struct rockchip_otp_data rk3588_data = {
301 .read = rockchip_rk3588_otp_read,
302 .offset = 0xC00,
303 .size = 0x400,
304 .block_size = 4,
305};
306
Finley Xiaoa907dc32019-09-25 17:57:49 +0200307static const struct udevice_id rockchip_otp_ids[] = {
308 {
309 .compatible = "rockchip,px30-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000310 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200311 },
312 {
313 .compatible = "rockchip,rk3308-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000314 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200315 },
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000316 {
317 .compatible = "rockchip,rk3568-otp",
318 .data = (ulong)&rk3568_data,
319 },
Jonas Karlmanf8880982023-02-22 22:44:39 +0000320 {
321 .compatible = "rockchip,rk3588-otp",
322 .data = (ulong)&rk3588_data,
323 },
Finley Xiaoa907dc32019-09-25 17:57:49 +0200324 {}
325};
326
327U_BOOT_DRIVER(rockchip_otp) = {
328 .name = "rockchip_otp",
329 .id = UCLASS_MISC,
330 .of_match = rockchip_otp_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700331 .of_to_plat = rockchip_otp_of_to_plat,
Jonas Karlman8fa18702023-02-22 22:44:38 +0000332 .plat_auto = sizeof(struct rockchip_otp_plat),
333 .ops = &rockchip_otp_ops,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200334};