aboutsummaryrefslogtreecommitdiff
path: root/drivers/rng/rockchip_rng.c
blob: 705b424cf3dd9a8f70df147a089b2906aea45f67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2020 Fuzhou Rockchip Electronics Co., Ltd
 */
#include <asm/arch-rockchip/hardware.h>
#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <linux/string.h>
#include <rng.h>

#define RK_HW_RNG_MAX 32

#define _SBF(s, v)	((v) << (s))

/* start of CRYPTO V1 register define */
#define CRYPTO_V1_CTRL				0x0008
#define CRYPTO_V1_RNG_START			BIT(8)
#define CRYPTO_V1_RNG_FLUSH			BIT(9)

#define CRYPTO_V1_TRNG_CTRL			0x0200
#define CRYPTO_V1_OSC_ENABLE			BIT(16)
#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x)		(x)

#define CRYPTO_V1_TRNG_DOUT_0			0x0204
/* end of CRYPTO V1 register define */

/* start of CRYPTO V2 register define */
#define CRYPTO_V2_RNG_CTL			0x0400
#define CRYPTO_V2_RNG_64_BIT_LEN		_SBF(4, 0x00)
#define CRYPTO_V2_RNG_128_BIT_LEN		_SBF(4, 0x01)
#define CRYPTO_V2_RNG_192_BIT_LEN		_SBF(4, 0x02)
#define CRYPTO_V2_RNG_256_BIT_LEN		_SBF(4, 0x03)
#define CRYPTO_V2_RNG_FATESY_SOC_RING		_SBF(2, 0x00)
#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0		_SBF(2, 0x01)
#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1		_SBF(2, 0x02)
#define CRYPTO_V2_RNG_SLOWEST_SOC_RING		_SBF(2, 0x03)
#define CRYPTO_V2_RNG_ENABLE			BIT(1)
#define CRYPTO_V2_RNG_START			BIT(0)
#define CRYPTO_V2_RNG_SAMPLE_CNT		0x0404
#define CRYPTO_V2_RNG_DOUT_0			0x0410
/* end of CRYPTO V2 register define */

/* start of TRNG V1 register define */
#define TRNG_V1_CTRL				0x0000
#define TRNG_V1_CTRL_NOP			_SBF(0, 0x00)
#define TRNG_V1_CTRL_RAND			_SBF(0, 0x01)
#define TRNG_V1_CTRL_SEED			_SBF(0, 0x02)

#define TRNG_V1_MODE				0x0008
#define TRNG_V1_MODE_128_BIT			_SBF(3, 0x00)
#define TRNG_V1_MODE_256_BIT			_SBF(3, 0x01)

#define TRNG_V1_IE				0x0010
#define TRNG_V1_IE_GLBL_EN			BIT(31)
#define TRNG_V1_IE_SEED_DONE_EN			BIT(1)
#define TRNG_V1_IE_RAND_RDY_EN			BIT(0)

#define TRNG_V1_ISTAT				0x0014
#define TRNG_V1_ISTAT_RAND_RDY			BIT(0)

/* RAND0 ~ RAND7 */
#define TRNG_V1_RAND0				0x0020
#define TRNG_V1_RAND7				0x003C

#define TRNG_V1_AUTO_RQSTS			0x0060

#define TRNG_V1_VERSION				0x00F0
#define TRNG_v1_VERSION_CODE			0x46BC
/* end of TRNG V1 register define */

#define RK_RNG_TIME_OUT	50000  /* max 50ms */

#define trng_write(pdata, pos, val)	writel(val, (pdata)->base + (pos))
#define trng_read(pdata, pos)		readl((pdata)->base + (pos))

struct rk_rng_soc_data {
	int (*rk_rng_init)(struct udevice *dev);
	int (*rk_rng_read)(struct udevice *dev, void *data, size_t len);
};

struct rk_rng_plat {
	fdt_addr_t base;
	struct rk_rng_soc_data *soc_data;
};

static int rk_rng_read_regs(fdt_addr_t addr, void *buf, size_t size)
{
	u32 count = RK_HW_RNG_MAX / sizeof(u32);
	u32 reg, tmp_len;

	if (size > RK_HW_RNG_MAX)
		return -EINVAL;

	while (size && count) {
		reg = readl(addr);
		tmp_len = min(size, sizeof(u32));
		memcpy(buf, &reg, tmp_len);
		addr += sizeof(u32);
		buf += tmp_len;
		size -= tmp_len;
		count--;
	}

	return 0;
}

static int rk_cryptov1_rng_read(struct udevice *dev, void *data, size_t len)
{
	struct rk_rng_plat *pdata = dev_get_priv(dev);
	u32 reg = 0;
	int retval;

	if (len > RK_HW_RNG_MAX)
		return -EINVAL;

	/* enable osc_ring to get entropy, sample period is set as 100 */
	writel(CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100),
	       pdata->base + CRYPTO_V1_TRNG_CTRL);

	rk_clrsetreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START,
		     CRYPTO_V1_RNG_START);

	retval = readl_poll_timeout(pdata->base + CRYPTO_V1_CTRL, reg,
				    !(reg & CRYPTO_V1_RNG_START),
				    RK_RNG_TIME_OUT);
	if (retval)
		goto exit;

	rk_rng_read_regs(pdata->base + CRYPTO_V1_TRNG_DOUT_0, data, len);

exit:
	/* close TRNG */
	rk_clrreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START);

	return 0;
}

static int rk_cryptov2_rng_read(struct udevice *dev, void *data, size_t len)
{
	struct rk_rng_plat *pdata = dev_get_priv(dev);
	u32 reg = 0;
	int retval;

	if (len > RK_HW_RNG_MAX)
		return -EINVAL;

	/* enable osc_ring to get entropy, sample period is set as 100 */
	writel(100, pdata->base + CRYPTO_V2_RNG_SAMPLE_CNT);

	reg |= CRYPTO_V2_RNG_256_BIT_LEN;
	reg |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
	reg |= CRYPTO_V2_RNG_ENABLE;
	reg |= CRYPTO_V2_RNG_START;

	rk_clrsetreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff, reg);

	retval = readl_poll_timeout(pdata->base + CRYPTO_V2_RNG_CTL, reg,
				    !(reg & CRYPTO_V2_RNG_START),
				    RK_RNG_TIME_OUT);
	if (retval)
		goto exit;

	rk_rng_read_regs(pdata->base + CRYPTO_V2_RNG_DOUT_0, data, len);

exit:
	/* close TRNG */
	rk_clrreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff);

	return retval;
}

static int rk_trngv1_init(struct udevice *dev)
{
	u32 status, version;
	u32 auto_reseed_cnt = 1000;
	struct rk_rng_plat *pdata = dev_get_priv(dev);

	version = trng_read(pdata, TRNG_V1_VERSION);
	if (version != TRNG_v1_VERSION_CODE) {
		printf("wrong trng version, expected = %08x, actual = %08x",
		       TRNG_V1_VERSION, version);
		return -EFAULT;
	}

	/* wait in case of RND_RDY triggered at firs power on */
	readl_poll_timeout(pdata->base + TRNG_V1_ISTAT, status,
			   (status & TRNG_V1_ISTAT_RAND_RDY),
			   RK_RNG_TIME_OUT);

	/* clear RAND_RDY flag for first power on */
	trng_write(pdata, TRNG_V1_ISTAT, status);

	/* auto reseed after (auto_reseed_cnt * 16) byte rand generate */
	trng_write(pdata, TRNG_V1_AUTO_RQSTS, auto_reseed_cnt);

	return 0;
}

static int rk_trngv1_rng_read(struct udevice *dev, void *data, size_t len)
{
	struct rk_rng_plat *pdata = dev_get_priv(dev);
	u32 reg = 0;
	int retval;

	if (len > RK_HW_RNG_MAX)
		return -EINVAL;

	trng_write(pdata, TRNG_V1_MODE, TRNG_V1_MODE_256_BIT);
	trng_write(pdata, TRNG_V1_CTRL, TRNG_V1_CTRL_RAND);

	retval = readl_poll_timeout(pdata->base + TRNG_V1_ISTAT, reg,
				    (reg & TRNG_V1_ISTAT_RAND_RDY),
				    RK_RNG_TIME_OUT);
	/* clear ISTAT */
	trng_write(pdata, TRNG_V1_ISTAT, reg);

	if (retval)
		goto exit;

	rk_rng_read_regs(pdata->base + TRNG_V1_RAND0, data, len);

exit:
	/* close TRNG */
	trng_write(pdata, TRNG_V1_CTRL, TRNG_V1_CTRL_NOP);

	return retval;
}

static int rockchip_rng_read(struct udevice *dev, void *data, size_t len)
{
	unsigned char *buf = data;
	unsigned int i;
	int ret = -EIO;

	struct rk_rng_plat *pdata = dev_get_priv(dev);

	if (!len)
		return 0;

	if (!pdata->soc_data || !pdata->soc_data->rk_rng_read)
		return -EINVAL;

	for (i = 0; i < len / RK_HW_RNG_MAX; i++, buf += RK_HW_RNG_MAX) {
		ret = pdata->soc_data->rk_rng_read(dev, buf, RK_HW_RNG_MAX);
		if (ret)
			goto exit;
	}

	if (len % RK_HW_RNG_MAX)
		ret = pdata->soc_data->rk_rng_read(dev, buf,
						   len % RK_HW_RNG_MAX);

exit:
	return ret;
}

static int rockchip_rng_of_to_plat(struct udevice *dev)
{
	struct rk_rng_plat *pdata = dev_get_priv(dev);

	memset(pdata, 0x00, sizeof(*pdata));

	pdata->base = (fdt_addr_t)dev_read_addr_ptr(dev);
	if (!pdata->base)
		return -ENOMEM;

	return 0;
}

static int rockchip_rng_probe(struct udevice *dev)
{
	struct rk_rng_plat *pdata = dev_get_priv(dev);
	int ret = 0;

	pdata->soc_data = (struct rk_rng_soc_data *)dev_get_driver_data(dev);

	if (pdata->soc_data->rk_rng_init)
		ret = pdata->soc_data->rk_rng_init(dev);

	return ret;
}

static const struct rk_rng_soc_data rk_cryptov1_soc_data = {
	.rk_rng_read = rk_cryptov1_rng_read,
};

static const struct rk_rng_soc_data rk_cryptov2_soc_data = {
	.rk_rng_read = rk_cryptov2_rng_read,
};

static const struct rk_rng_soc_data rk_trngv1_soc_data = {
	.rk_rng_init = rk_trngv1_init,
	.rk_rng_read = rk_trngv1_rng_read,
};

static const struct dm_rng_ops rockchip_rng_ops = {
	.read = rockchip_rng_read,
};

static const struct udevice_id rockchip_rng_match[] = {
	{
		.compatible = "rockchip,cryptov1-rng",
		.data = (ulong)&rk_cryptov1_soc_data,
	},
	{
		.compatible = "rockchip,cryptov2-rng",
		.data = (ulong)&rk_cryptov2_soc_data,
	},
	{
		.compatible = "rockchip,trngv1",
		.data = (ulong)&rk_trngv1_soc_data,
	},
	{},
};

U_BOOT_DRIVER(rockchip_rng) = {
	.name = "rockchip-rng",
	.id = UCLASS_RNG,
	.of_match = rockchip_rng_match,
	.ops = &rockchip_rng_ops,
	.probe = rockchip_rng_probe,
	.of_to_plat = rockchip_rng_of_to_plat,
	.priv_auto	= sizeof(struct rk_rng_plat),
};