1 // 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>
8 #include <command.h>
9 #include <display_options.h>
10 #include <dm.h>
11 #include <linux/bitops.h>
12 #include <linux/delay.h>
13 #include <linux/iopoll.h>
14 #include <malloc.h>
15 #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
53 #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
64 struct rockchip_otp_plat {
65 void __iomem *base;
66 };
67
68 struct rockchip_otp_data {
69 int (*read)(struct udevice *dev, int offset, void *buf, int size);
70 int offset;
71 int size;
72 int block_size;
73 };
74
75 #if defined(DEBUG)
dump_otp(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])76 static 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));
92 if (ret <= 0)
93 return 0;
94
95 print_buffer(i, data, 1, sizeof(data), sizeof(data));
96 }
97
98 return 0;
99 }
100
101 U_BOOT_CMD(
102 dump_otp, 1, 1, dump_otp,
103 "Dump the content of the otp",
104 ""
105 );
106 #endif
107
rockchip_otp_poll_timeout(struct rockchip_otp_plat * otp,u32 flag,u32 reg)108 static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp,
109 u32 flag, u32 reg)
110 {
111 u32 status;
112 int ret;
113
114 ret = readl_poll_sleep_timeout(otp->base + reg, status,
115 (status & flag), 1, OTPC_TIMEOUT);
116 if (ret)
117 return ret;
118
119 /* Clear int flag */
120 writel(flag, otp->base + reg);
121
122 return 0;
123 }
124
rockchip_otp_ecc_enable(struct rockchip_otp_plat * otp,bool enable)125 static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable)
126 {
127 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
141 return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE, OTPC_INT_STATUS);
142 }
143
rockchip_px30_otp_read(struct udevice * dev,int offset,void * buf,int size)144 static int rockchip_px30_otp_read(struct udevice *dev, int offset,
145 void *buf, int size)
146 {
147 struct rockchip_otp_plat *otp = dev_get_plat(dev);
148 u8 *buffer = buf;
149 int ret;
150
151 ret = rockchip_otp_ecc_enable(otp, false);
152 if (ret)
153 return ret;
154
155 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
156 udelay(5);
157
158 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
164 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
165 OTPC_INT_STATUS);
166 if (ret)
167 goto read_end;
168
169 *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF);
170 }
171
172 read_end:
173 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
174
175 return ret;
176 }
177
rockchip_rk3568_otp_read(struct udevice * dev,int offset,void * buf,int size)178 static 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
198 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE,
199 OTPC_INT_STATUS);
200 if (ret)
201 goto read_end;
202
203 *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF);
204 }
205
206 read_end:
207 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
208
209 return ret;
210 }
211
rockchip_rk3588_otp_read(struct udevice * dev,int offset,void * buf,int size)212 static 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
rockchip_otp_read(struct udevice * dev,int offset,void * buf,int size)235 static int rockchip_otp_read(struct udevice *dev, int offset,
236 void *buf, int size)
237 {
238 const struct rockchip_otp_data *data =
239 (void *)dev_get_driver_data(dev);
240 u32 block_start, block_end, block_offset, blocks;
241 u8 *buffer;
242 int ret;
243
244 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
245 return -EINVAL;
246
247 if (!data->read)
248 return -ENOSYS;
249
250 offset += data->offset;
251
252 if (data->block_size <= 1) {
253 ret = data->read(dev, offset, buf, size);
254 goto done;
255 }
256
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);
271
272 done:
273 return ret < 0 ? ret : size;
274 }
275
276 static const struct misc_ops rockchip_otp_ops = {
277 .read = rockchip_otp_read,
278 };
279
rockchip_otp_of_to_plat(struct udevice * dev)280 static int rockchip_otp_of_to_plat(struct udevice *dev)
281 {
282 struct rockchip_otp_plat *plat = dev_get_plat(dev);
283
284 plat->base = dev_read_addr_ptr(dev);
285
286 return 0;
287 }
288
289 static const struct rockchip_otp_data px30_data = {
290 .read = rockchip_px30_otp_read,
291 .size = 0x40,
292 };
293
294 static const struct rockchip_otp_data rk3568_data = {
295 .read = rockchip_rk3568_otp_read,
296 .size = 0x80,
297 .block_size = 2,
298 };
299
300 static 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
307 static const struct udevice_id rockchip_otp_ids[] = {
308 {
309 .compatible = "rockchip,px30-otp",
310 .data = (ulong)&px30_data,
311 },
312 {
313 .compatible = "rockchip,rk3308-otp",
314 .data = (ulong)&px30_data,
315 },
316 {
317 .compatible = "rockchip,rk3568-otp",
318 .data = (ulong)&rk3568_data,
319 },
320 {
321 .compatible = "rockchip,rk3588-otp",
322 .data = (ulong)&rk3588_data,
323 },
324 {}
325 };
326
327 U_BOOT_DRIVER(rockchip_otp) = {
328 .name = "rockchip_otp",
329 .id = UCLASS_MISC,
330 .of_match = rockchip_otp_ids,
331 .of_to_plat = rockchip_otp_of_to_plat,
332 .plat_auto = sizeof(struct rockchip_otp_plat),
333 .ops = &rockchip_otp_ops,
334 };
335