blob: fea3ca7c28827ac94954d8872b56dce2fb191c88 [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>
Finley Xiaoa907dc32019-09-25 17:57:49 +02008#include <dm.h>
9#include <linux/bitops.h>
10#include <linux/delay.h>
Jonas Karlman8fa18702023-02-22 22:44:38 +000011#include <linux/iopoll.h>
Jonas Karlmand58d55d2023-02-22 22:44:39 +000012#include <malloc.h>
Finley Xiaoa907dc32019-09-25 17:57:49 +020013#include <misc.h>
14
15/* OTP Register Offsets */
16#define OTPC_SBPI_CTRL 0x0020
17#define OTPC_SBPI_CMD_VALID_PRE 0x0024
18#define OTPC_SBPI_CS_VALID_PRE 0x0028
19#define OTPC_SBPI_STATUS 0x002C
20#define OTPC_USER_CTRL 0x0100
21#define OTPC_USER_ADDR 0x0104
22#define OTPC_USER_ENABLE 0x0108
23#define OTPC_USER_QP 0x0120
24#define OTPC_USER_Q 0x0124
25#define OTPC_INT_STATUS 0x0304
26#define OTPC_SBPI_CMD0_OFFSET 0x1000
27#define OTPC_SBPI_CMD1_OFFSET 0x1004
28
29/* OTP Register bits and masks */
30#define OTPC_USER_ADDR_MASK GENMASK(31, 16)
31#define OTPC_USE_USER BIT(0)
32#define OTPC_USE_USER_MASK GENMASK(16, 16)
33#define OTPC_USER_FSM_ENABLE BIT(0)
34#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16)
35#define OTPC_SBPI_DONE BIT(1)
36#define OTPC_USER_DONE BIT(2)
37
38#define SBPI_DAP_ADDR 0x02
39#define SBPI_DAP_ADDR_SHIFT 8
40#define SBPI_DAP_ADDR_MASK GENMASK(31, 24)
41#define SBPI_CMD_VALID_MASK GENMASK(31, 16)
42#define SBPI_DAP_CMD_WRF 0xC0
43#define SBPI_DAP_REG_ECC 0x3A
44#define SBPI_ECC_ENABLE 0x00
45#define SBPI_ECC_DISABLE 0x09
46#define SBPI_ENABLE BIT(0)
47#define SBPI_ENABLE_MASK GENMASK(16, 16)
48
49#define OTPC_TIMEOUT 10000
50
Simon Glass8a8d24b2020-12-03 16:55:23 -070051struct rockchip_otp_plat {
Finley Xiaoa907dc32019-09-25 17:57:49 +020052 void __iomem *base;
Finley Xiaoa907dc32019-09-25 17:57:49 +020053};
54
Jonas Karlman8fa18702023-02-22 22:44:38 +000055struct rockchip_otp_data {
56 int (*read)(struct udevice *dev, int offset, void *buf, int size);
57 int size;
Jonas Karlmand58d55d2023-02-22 22:44:39 +000058 int block_size;
Jonas Karlman8fa18702023-02-22 22:44:38 +000059};
60
61static int rockchip_otp_poll_timeout(struct rockchip_otp_plat *otp, u32 flag)
Finley Xiaoa907dc32019-09-25 17:57:49 +020062{
Jonas Karlman8fa18702023-02-22 22:44:38 +000063 u32 status;
64 int ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +020065
Jonas Karlman8fa18702023-02-22 22:44:38 +000066 ret = readl_poll_sleep_timeout(otp->base + OTPC_INT_STATUS, status,
67 (status & flag), 1, OTPC_TIMEOUT);
68 if (ret)
69 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +020070
Jonas Karlman8fa18702023-02-22 22:44:38 +000071 /* Clear int flag */
Finley Xiaoa907dc32019-09-25 17:57:49 +020072 writel(flag, otp->base + OTPC_INT_STATUS);
73
74 return 0;
75}
76
Jonas Karlman8fa18702023-02-22 22:44:38 +000077static int rockchip_otp_ecc_enable(struct rockchip_otp_plat *otp, bool enable)
Finley Xiaoa907dc32019-09-25 17:57:49 +020078{
Finley Xiaoa907dc32019-09-25 17:57:49 +020079 writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
80 otp->base + OTPC_SBPI_CTRL);
81
82 writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
83 writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
84 otp->base + OTPC_SBPI_CMD0_OFFSET);
85
86 if (enable)
87 writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
88 else
89 writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
90
91 writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
92
Jonas Karlman8fa18702023-02-22 22:44:38 +000093 return rockchip_otp_poll_timeout(otp, OTPC_SBPI_DONE);
Finley Xiaoa907dc32019-09-25 17:57:49 +020094}
95
96static int rockchip_px30_otp_read(struct udevice *dev, int offset,
97 void *buf, int size)
98{
Simon Glass8a8d24b2020-12-03 16:55:23 -070099 struct rockchip_otp_plat *otp = dev_get_plat(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200100 u8 *buffer = buf;
Jonas Karlman8fa18702023-02-22 22:44:38 +0000101 int ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200102
103 ret = rockchip_otp_ecc_enable(otp, false);
Jonas Karlman8fa18702023-02-22 22:44:38 +0000104 if (ret)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200105 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200106
107 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
108 udelay(5);
Jonas Karlman8fa18702023-02-22 22:44:38 +0000109
Finley Xiaoa907dc32019-09-25 17:57:49 +0200110 while (size--) {
111 writel(offset++ | OTPC_USER_ADDR_MASK,
112 otp->base + OTPC_USER_ADDR);
113 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
114 otp->base + OTPC_USER_ENABLE);
115
Jonas Karlman8fa18702023-02-22 22:44:38 +0000116 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE);
117 if (ret)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200118 goto read_end;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200119
Jonas Karlman8fa18702023-02-22 22:44:38 +0000120 *buffer++ = (u8)(readl(otp->base + OTPC_USER_Q) & 0xFF);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200121 }
122
123read_end:
124 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
125
126 return ret;
127}
128
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000129static int rockchip_rk3568_otp_read(struct udevice *dev, int offset,
130 void *buf, int size)
131{
132 struct rockchip_otp_plat *otp = dev_get_plat(dev);
133 u16 *buffer = buf;
134 int ret;
135
136 ret = rockchip_otp_ecc_enable(otp, false);
137 if (ret)
138 return ret;
139
140 writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
141 udelay(5);
142
143 while (size--) {
144 writel(offset++ | OTPC_USER_ADDR_MASK,
145 otp->base + OTPC_USER_ADDR);
146 writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
147 otp->base + OTPC_USER_ENABLE);
148
149 ret = rockchip_otp_poll_timeout(otp, OTPC_USER_DONE);
150 if (ret)
151 goto read_end;
152
153 *buffer++ = (u16)(readl(otp->base + OTPC_USER_Q) & 0xFFFF);
154 }
155
156read_end:
157 writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
158
159 return ret;
160}
161
Finley Xiaoa907dc32019-09-25 17:57:49 +0200162static int rockchip_otp_read(struct udevice *dev, int offset,
163 void *buf, int size)
164{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000165 const struct rockchip_otp_data *data =
166 (void *)dev_get_driver_data(dev);
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000167 u32 block_start, block_end, block_offset, blocks;
168 u8 *buffer;
169 int ret;
Jonas Karlman8fa18702023-02-22 22:44:38 +0000170
171 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
172 return -EINVAL;
173
174 if (!data->read)
175 return -ENOSYS;
176
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000177 if (data->block_size <= 1)
178 return data->read(dev, offset, buf, size);
179
180 block_start = offset / data->block_size;
181 block_offset = offset % data->block_size;
182 block_end = DIV_ROUND_UP(offset + size, data->block_size);
183 blocks = block_end - block_start;
184
185 buffer = calloc(blocks, data->block_size);
186 if (!buffer)
187 return -ENOMEM;
188
189 ret = data->read(dev, block_start, buffer, blocks);
190 if (!ret)
191 memcpy(buf, buffer + block_offset, size);
192
193 free(buffer);
194 return ret;
Finley Xiaoa907dc32019-09-25 17:57:49 +0200195}
196
197static const struct misc_ops rockchip_otp_ops = {
198 .read = rockchip_otp_read,
199};
200
Simon Glassd1998a92020-12-03 16:55:21 -0700201static int rockchip_otp_of_to_plat(struct udevice *dev)
Finley Xiaoa907dc32019-09-25 17:57:49 +0200202{
Jonas Karlman8fa18702023-02-22 22:44:38 +0000203 struct rockchip_otp_plat *plat = dev_get_plat(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200204
Jonas Karlman8fa18702023-02-22 22:44:38 +0000205 plat->base = dev_read_addr_ptr(dev);
Finley Xiaoa907dc32019-09-25 17:57:49 +0200206
207 return 0;
208}
209
Jonas Karlman8fa18702023-02-22 22:44:38 +0000210static const struct rockchip_otp_data px30_data = {
211 .read = rockchip_px30_otp_read,
212 .size = 0x40,
213};
214
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000215static const struct rockchip_otp_data rk3568_data = {
216 .read = rockchip_rk3568_otp_read,
217 .size = 0x80,
218 .block_size = 2,
219};
220
Finley Xiaoa907dc32019-09-25 17:57:49 +0200221static const struct udevice_id rockchip_otp_ids[] = {
222 {
223 .compatible = "rockchip,px30-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000224 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200225 },
226 {
227 .compatible = "rockchip,rk3308-otp",
Jonas Karlman8fa18702023-02-22 22:44:38 +0000228 .data = (ulong)&px30_data,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200229 },
Jonas Karlmand58d55d2023-02-22 22:44:39 +0000230 {
231 .compatible = "rockchip,rk3568-otp",
232 .data = (ulong)&rk3568_data,
233 },
Finley Xiaoa907dc32019-09-25 17:57:49 +0200234 {}
235};
236
237U_BOOT_DRIVER(rockchip_otp) = {
238 .name = "rockchip_otp",
239 .id = UCLASS_MISC,
240 .of_match = rockchip_otp_ids,
Simon Glassd1998a92020-12-03 16:55:21 -0700241 .of_to_plat = rockchip_otp_of_to_plat,
Jonas Karlman8fa18702023-02-22 22:44:38 +0000242 .plat_auto = sizeof(struct rockchip_otp_plat),
243 .ops = &rockchip_otp_ops,
Finley Xiaoa907dc32019-09-25 17:57:49 +0200244};