blob: c19cd5ce625e71bbdbab4ad1fe5202d2b20510f1 [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));
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
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
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000252 if (data->block_size <= 1)
253 return data->read(dev, offset, buf, size);
254
255 block_start = offset / data->block_size;
256 block_offset = offset % data->block_size;
257 block_end = DIV_ROUND_UP(offset + size, data->block_size);
258 blocks = block_end - block_start;
259
260 buffer = calloc(blocks, data->block_size);
261 if (!buffer)
262 return -ENOMEM;
263
264 ret = data->read(dev, block_start, buffer, blocks);
265 if (!ret)
266 memcpy(buf, buffer + block_offset, size);
267
268 free(buffer);
269 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200270}
271
272static const struct misc_ops rockchip_otp_ops = {
273 .read = rockchip_otp_read,
274};
275
Simon Glassd1998a92020-12-03 16:55:21 -0700276static int rockchip_otp_of_to_plat(struct udevice *dev)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200277{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000278 struct rockchip_otp_plat *plat = dev_get_plat(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200279
Jonas Karlman8fa18702023-02-22 22:44:38 +0000280 plat->base = dev_read_addr_ptr(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200281
282 return 0;
283}
284
Jonas Karlman8fa18702023-02-22 22:44:38 +0000285static const struct rockchip_otp_data px30_data = {
286 .read = rockchip_px30_otp_read,
287 .size = 0x40,
288};
289
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000290static const struct rockchip_otp_data rk3568_data = {
291 .read = rockchip_rk3568_otp_read,
292 .size = 0x80,
293 .block_size = 2,
294};
295
Jonas Karlmanf8880982023-02-22 22:44:39 +0000296static const struct rockchip_otp_data rk3588_data = {
297 .read = rockchip_rk3588_otp_read,
298 .offset = 0xC00,
299 .size = 0x400,
300 .block_size = 4,
301};
302
Finley Xiaoa907dc32019-09-25 17:57:49 +0200303static const struct udevice_id rockchip_otp_ids[] = {
304 {
305 .compatible = "rockchip,px30-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000306 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200307 },
308 {
309 .compatible = "rockchip,rk3308-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000310 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200311 },
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000312 {
313 .compatible = "rockchip,rk3568-otp",
314 .data = (ulong)&rk3568_data,
315 },
Jonas Karlmanf8880982023-02-22 22:44:39 +0000316 {
317 .compatible = "rockchip,rk3588-otp",
318 .data = (ulong)&rk3588_data,
319 },
Finley Xiaoa907dc32019-09-25 17:57:49 +0200320 {}
321};
322
323U_BOOT_DRIVER(rockchip_otp) = {
324 .name = "rockchip_otp",
325 .id = UCLASS_MISC,
326 .of_match = rockchip_otp_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700327 .of_to_plat = rockchip_otp_of_to_plat,
Jonas Karlman8fa18702023-02-22 22:44:38 +0000328 .plat_auto = sizeof(struct rockchip_otp_plat),
329 .ops = &rockchip_otp_ops,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200330};