summaryrefslogtreecommitdiff
path: root/drivers/rng/rockchip_rng.c
blob: 800150f1147984898f1b82d05883e6a66481c101 (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
// 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 */

#define RK_RNG_TIME_OUT	50000  /* max 50ms */

struct rk_rng_soc_data {
	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_v1_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_v2_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 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);

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

	return 0;
}

static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
	.rk_rng_read = rk_v1_rng_read,
};

static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
	.rk_rng_read = rk_v2_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_rng_v1_soc_data,
	},
	{
		.compatible = "rockchip,cryptov2-rng",
		.data = (ulong)&rk_rng_v2_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),
};