blob: 8cf61e36aeff8e1eedbebda0177dcd655ec6d4b8 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Philipp Tomsichfcc1d632017-05-05 19:21:38 +02002/*
3 * eFuse driver for Rockchip devices
4 *
5 * Copyright 2017, Theobroma Systems Design und Consulting GmbH
6 * Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Philipp Tomsichfcc1d632017-05-05 19:21:38 +02007 */
8
9#include <common.h>
10#include <asm/io.h>
11#include <command.h>
12#include <display_options.h>
13#include <dm.h>
14#include <linux/bitops.h>
15#include <linux/delay.h>
Jonas Karlmanc0189652023-02-22 22:44:40 +000016#include <linux/iopoll.h>
Jonas Karlman26ed3852023-02-22 22:44:39 +000017#include <malloc.h>
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020018#include <misc.h>
19
Jonas Karlman26ed3852023-02-22 22:44:39 +000020#define EFUSE_CTRL 0x0000
Jonas Karlman14ad36d2023-02-22 22:44:40 +000021#define RK3288_A_SHIFT 6
22#define RK3288_A_MASK GENMASK(15, 6)
23#define RK3288_ADDR(n) ((n) << RK3288_A_SHIFT)
Jonas Karlman26ed3852023-02-22 22:44:39 +000024#define RK3399_A_SHIFT 16
25#define RK3399_A_MASK GENMASK(25, 16)
26#define RK3399_ADDR(n) ((n) << RK3399_A_SHIFT)
27#define RK3399_STROBSFTSEL BIT(9)
28#define RK3399_RSB BIT(7)
29#define RK3399_PD BIT(5)
Jonas Karlman14ad36d2023-02-22 22:44:40 +000030#define EFUSE_PGENB BIT(3)
31#define EFUSE_LOAD BIT(2)
32#define EFUSE_STROBE BIT(1)
33#define EFUSE_CSB BIT(0)
Jonas Karlman26ed3852023-02-22 22:44:39 +000034#define EFUSE_DOUT 0x0004
Jonas Karlmanc0189652023-02-22 22:44:40 +000035#define RK3328_INT_STATUS 0x0018
36#define RK3328_INT_FINISH BIT(0)
37#define RK3328_DOUT 0x0020
38#define RK3328_AUTO_CTRL 0x0024
39#define RK3328_AUTO_RD BIT(1)
40#define RK3328_AUTO_ENB BIT(0)
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020041
Simon Glassb75b15b2020-12-03 16:55:23 -070042struct rockchip_efuse_plat {
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020043 void __iomem *base;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020044};
45
Jonas Karlman26ed3852023-02-22 22:44:39 +000046struct rockchip_efuse_data {
47 int (*read)(struct udevice *dev, int offset, void *buf, int size);
Jonas Karlmanc0189652023-02-22 22:44:40 +000048 int offset;
Jonas Karlman26ed3852023-02-22 22:44:39 +000049 int size;
50 int block_size;
51};
52
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020053#if defined(DEBUG)
Jonas Karlman26ed3852023-02-22 22:44:39 +000054static int dump_efuse(struct cmd_tbl *cmdtp, int flag,
55 int argc, char *const argv[])
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020056{
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020057 struct udevice *dev;
Jonas Karlman26ed3852023-02-22 22:44:39 +000058 u8 data[4];
59 int ret, i;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020060
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020061 ret = uclass_get_device_by_driver(UCLASS_MISC,
Simon Glass65130cd2020-12-28 20:34:56 -070062 DM_DRIVER_GET(rockchip_efuse), &dev);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020063 if (ret) {
64 printf("%s: no misc-device found\n", __func__);
65 return 0;
66 }
67
Jonas Karlman26ed3852023-02-22 22:44:39 +000068 for (i = 0; true; i += sizeof(data)) {
69 ret = misc_read(dev, i, &data, sizeof(data));
70 if (ret < 0)
71 return 0;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020072
Jonas Karlman26ed3852023-02-22 22:44:39 +000073 print_buffer(i, data, 1, sizeof(data), sizeof(data));
74 }
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020075
76 return 0;
77}
78
79U_BOOT_CMD(
Jonas Karlman26ed3852023-02-22 22:44:39 +000080 dump_efuse, 1, 1, dump_efuse,
81 "Dump the content of the efuse",
Philipp Tomsichfcc1d632017-05-05 19:21:38 +020082 ""
83);
84#endif
85
Jonas Karlman14ad36d2023-02-22 22:44:40 +000086static int rockchip_rk3288_efuse_read(struct udevice *dev, int offset,
87 void *buf, int size)
88{
89 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
90 u8 *buffer = buf;
91
92 /* Switch to read mode */
93 writel(EFUSE_CSB, efuse->base + EFUSE_CTRL);
94 writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
95 udelay(2);
96
97 while (size--) {
98 clrsetbits_le32(efuse->base + EFUSE_CTRL, RK3288_A_MASK,
99 RK3288_ADDR(offset++));
100 udelay(2);
101 setbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
102 udelay(2);
103 *buffer++ = (u8)(readl(efuse->base + EFUSE_DOUT) & 0xFF);
104 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
105 udelay(2);
106 }
107
108 /* Switch to standby mode */
109 writel(EFUSE_CSB | EFUSE_PGENB, efuse->base + EFUSE_CTRL);
110
111 return 0;
112}
113
Jonas Karlmanc0189652023-02-22 22:44:40 +0000114static int rockchip_rk3328_efuse_read(struct udevice *dev, int offset,
115 void *buf, int size)
116{
117 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
118 u32 status, *buffer = buf;
119 int ret;
120
121 while (size--) {
122 writel(RK3328_AUTO_RD | RK3328_AUTO_ENB | RK3399_ADDR(offset++),
123 efuse->base + RK3328_AUTO_CTRL);
124 udelay(1);
125
126 ret = readl_poll_sleep_timeout(efuse->base + RK3328_INT_STATUS,
127 status, (status & RK3328_INT_FINISH), 1, 50);
128 if (ret)
129 return ret;
130
131 *buffer++ = readl(efuse->base + RK3328_DOUT);
132 writel(RK3328_INT_FINISH, efuse->base + RK3328_INT_STATUS);
133 }
134
135 return 0;
136}
137
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200138static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
139 void *buf, int size)
140{
Jonas Karlman26ed3852023-02-22 22:44:39 +0000141 struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
142 u32 *buffer = buf;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200143
Jonas Karlman26ed3852023-02-22 22:44:39 +0000144 /* Switch to array read mode */
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000145 writel(EFUSE_LOAD | EFUSE_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
Jonas Karlman26ed3852023-02-22 22:44:39 +0000146 efuse->base + EFUSE_CTRL);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200147 udelay(1);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000148
149 while (size--) {
150 setbits_le32(efuse->base + EFUSE_CTRL,
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000151 EFUSE_STROBE | RK3399_ADDR(offset++));
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200152 udelay(1);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000153 *buffer++ = readl(efuse->base + EFUSE_DOUT);
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000154 clrbits_le32(efuse->base + EFUSE_CTRL, EFUSE_STROBE);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200155 udelay(1);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200156 }
157
Jonas Karlman26ed3852023-02-22 22:44:39 +0000158 /* Switch to power-down mode */
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000159 writel(RK3399_PD | EFUSE_CSB, efuse->base + EFUSE_CTRL);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200160
161 return 0;
162}
163
164static int rockchip_efuse_read(struct udevice *dev, int offset,
165 void *buf, int size)
166{
Jonas Karlman26ed3852023-02-22 22:44:39 +0000167 const struct rockchip_efuse_data *data =
168 (void *)dev_get_driver_data(dev);
169 u32 block_start, block_end, block_offset, blocks;
170 u8 *buffer;
171 int ret;
172
173 if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
174 return -EINVAL;
175
176 if (!data->read)
177 return -ENOSYS;
178
Jonas Karlmanc0189652023-02-22 22:44:40 +0000179 offset += data->offset;
180
Jonas Karlman26ed3852023-02-22 22:44:39 +0000181 if (data->block_size <= 1)
182 return data->read(dev, offset, buf, size);
183
184 block_start = offset / data->block_size;
185 block_offset = offset % data->block_size;
186 block_end = DIV_ROUND_UP(offset + size, data->block_size);
187 blocks = block_end - block_start;
188
189 buffer = calloc(blocks, data->block_size);
190 if (!buffer)
191 return -ENOMEM;
192
193 ret = data->read(dev, block_start, buffer, blocks);
194 if (!ret)
195 memcpy(buf, buffer + block_offset, size);
196
197 free(buffer);
198 return ret;
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200199}
200
201static const struct misc_ops rockchip_efuse_ops = {
202 .read = rockchip_efuse_read,
203};
204
Simon Glassaad29ae2020-12-03 16:55:21 -0700205static int rockchip_efuse_of_to_plat(struct udevice *dev)
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200206{
Simon Glassb75b15b2020-12-03 16:55:23 -0700207 struct rockchip_efuse_plat *plat = dev_get_plat(dev);
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200208
Philipp Tomsich3b505962017-09-12 17:32:26 +0200209 plat->base = dev_read_addr_ptr(dev);
Jonas Karlman26ed3852023-02-22 22:44:39 +0000210
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200211 return 0;
212}
213
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000214static const struct rockchip_efuse_data rk3288_data = {
215 .read = rockchip_rk3288_efuse_read,
216 .size = 0x20,
Jonas Karlmanc0189652023-02-22 22:44:40 +0000217};
218
219static const struct rockchip_efuse_data rk3328_data = {
220 .read = rockchip_rk3328_efuse_read,
221 .offset = 0x60,
222 .size = 0x20,
223 .block_size = 4,
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000224};
225
Jonas Karlman26ed3852023-02-22 22:44:39 +0000226static const struct rockchip_efuse_data rk3399_data = {
227 .read = rockchip_rk3399_efuse_read,
228 .size = 0x80,
229 .block_size = 4,
230};
231
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200232static const struct udevice_id rockchip_efuse_ids[] = {
Jonas Karlman26ed3852023-02-22 22:44:39 +0000233 {
Jonas Karlman14ad36d2023-02-22 22:44:40 +0000234 .compatible = "rockchip,rk3066a-efuse",
235 .data = (ulong)&rk3288_data,
236 },
237 {
238 .compatible = "rockchip,rk3188-efuse",
239 .data = (ulong)&rk3288_data,
240 },
241 {
242 .compatible = "rockchip,rk3228-efuse",
243 .data = (ulong)&rk3288_data,
244 },
245 {
246 .compatible = "rockchip,rk3288-efuse",
247 .data = (ulong)&rk3288_data,
248 },
249 {
Jonas Karlmanc0189652023-02-22 22:44:40 +0000250 .compatible = "rockchip,rk3328-efuse",
251 .data = (ulong)&rk3328_data,
252 },
253 {
Jonas Karlman26ed3852023-02-22 22:44:39 +0000254 .compatible = "rockchip,rk3399-efuse",
255 .data = (ulong)&rk3399_data,
256 },
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200257 {}
258};
259
260U_BOOT_DRIVER(rockchip_efuse) = {
261 .name = "rockchip_efuse",
262 .id = UCLASS_MISC,
263 .of_match = rockchip_efuse_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700264 .of_to_plat = rockchip_efuse_of_to_plat,
Jonas Karlman26ed3852023-02-22 22:44:39 +0000265 .plat_auto = sizeof(struct rockchip_efuse_plat),
Philipp Tomsichfcc1d632017-05-05 19:21:38 +0200266 .ops = &rockchip_efuse_ops,
267};