xref: /rk3399_rockchip-uboot/drivers/rng/rockchip_rng.c (revision c3e08fa050aa3e8eb31fbd76f4d1e28c00ffe0b4)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2020 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 #include <asm/arch-rockchip/hardware.h>
6 #include <asm/io.h>
7 #include <common.h>
8 #include <dm.h>
9 #include <linux/iopoll.h>
10 #include <linux/string.h>
11 #include <rng.h>
12 
13 #define RK_HW_RNG_MAX 32
14 
15 #define _SBF(s, v)	((v) << (s))
16 
17 /* start of CRYPTO V1 register define */
18 #define CRYPTO_V1_CTRL				0x0008
19 #define CRYPTO_V1_RNG_START			BIT(8)
20 #define CRYPTO_V1_RNG_FLUSH			BIT(9)
21 
22 #define CRYPTO_V1_TRNG_CTRL			0x0200
23 #define CRYPTO_V1_OSC_ENABLE			BIT(16)
24 #define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x)		(x)
25 
26 #define CRYPTO_V1_TRNG_DOUT_0			0x0204
27 /* end of CRYPTO V1 register define */
28 
29 /* start of CRYPTO V2 register define */
30 #define CRYPTO_V2_RNG_CTL			0x0400
31 #define CRYPTO_V2_RNG_64_BIT_LEN		_SBF(4, 0x00)
32 #define CRYPTO_V2_RNG_128_BIT_LEN		_SBF(4, 0x01)
33 #define CRYPTO_V2_RNG_192_BIT_LEN		_SBF(4, 0x02)
34 #define CRYPTO_V2_RNG_256_BIT_LEN		_SBF(4, 0x03)
35 #define CRYPTO_V2_RNG_FATESY_SOC_RING		_SBF(2, 0x00)
36 #define CRYPTO_V2_RNG_SLOWER_SOC_RING_0		_SBF(2, 0x01)
37 #define CRYPTO_V2_RNG_SLOWER_SOC_RING_1		_SBF(2, 0x02)
38 #define CRYPTO_V2_RNG_SLOWEST_SOC_RING		_SBF(2, 0x03)
39 #define CRYPTO_V2_RNG_ENABLE			BIT(1)
40 #define CRYPTO_V2_RNG_START			BIT(0)
41 #define CRYPTO_V2_RNG_SAMPLE_CNT		0x0404
42 #define CRYPTO_V2_RNG_DOUT_0			0x0410
43 /* end of CRYPTO V2 register define */
44 
45 #define RK_RNG_TIME_OUT	50000  /* max 50ms */
46 
47 struct rk_rng_soc_data {
48 	int (*rk_rng_read)(struct udevice *dev, void *data, size_t len);
49 };
50 
51 struct rk_rng_platdata {
52 	fdt_addr_t base;
53 	struct rk_rng_soc_data *soc_data;
54 };
55 
56 static int rk_rng_read_regs(fdt_addr_t addr, void *buf, size_t size)
57 {
58 	u32 count = RK_HW_RNG_MAX / sizeof(u32);
59 	u32 reg, tmp_len;
60 
61 	if (size > RK_HW_RNG_MAX)
62 		return -EINVAL;
63 
64 	while (size && count) {
65 		reg = readl(addr);
66 		tmp_len = min(size, sizeof(u32));
67 		memcpy(buf, &reg, tmp_len);
68 		addr += sizeof(u32);
69 		buf += tmp_len;
70 		size -= tmp_len;
71 		count--;
72 	}
73 
74 	return 0;
75 }
76 
77 static int rk_v1_rng_read(struct udevice *dev, void *data, size_t len)
78 {
79 	struct rk_rng_platdata *pdata = dev_get_priv(dev);
80 	u32 reg = 0;
81 	int retval;
82 
83 	if (len > RK_HW_RNG_MAX)
84 		return -EINVAL;
85 
86 	/* enable osc_ring to get entropy, sample period is set as 100 */
87 	writel(CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100),
88 	       pdata->base + CRYPTO_V1_TRNG_CTRL);
89 
90 	rk_clrsetreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START,
91 		     CRYPTO_V1_RNG_START);
92 
93 	retval = readl_poll_timeout(pdata->base + CRYPTO_V1_CTRL, reg,
94 				    !(reg & CRYPTO_V1_RNG_START),
95 				    RK_RNG_TIME_OUT);
96 	if (retval)
97 		goto exit;
98 
99 	rk_rng_read_regs(pdata->base + CRYPTO_V1_TRNG_DOUT_0, data, len);
100 
101 exit:
102 	/* close TRNG */
103 	rk_clrreg(pdata->base + CRYPTO_V1_CTRL, CRYPTO_V1_RNG_START);
104 
105 	return 0;
106 }
107 
108 static int rk_v2_rng_read(struct udevice *dev, void *data, size_t len)
109 {
110 	struct rk_rng_platdata *pdata = dev_get_priv(dev);
111 	u32 reg = 0;
112 	int retval;
113 
114 	if (len > RK_HW_RNG_MAX)
115 		return -EINVAL;
116 
117 	/* enable osc_ring to get entropy, sample period is set as 100 */
118 	writel(100, pdata->base + CRYPTO_V2_RNG_SAMPLE_CNT);
119 
120 	reg |= CRYPTO_V2_RNG_256_BIT_LEN;
121 	reg |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
122 	reg |= CRYPTO_V2_RNG_ENABLE;
123 	reg |= CRYPTO_V2_RNG_START;
124 
125 	rk_clrsetreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff, reg);
126 
127 	retval = readl_poll_timeout(pdata->base + CRYPTO_V2_RNG_CTL, reg,
128 				    !(reg & CRYPTO_V2_RNG_START),
129 				    RK_RNG_TIME_OUT);
130 	if (retval)
131 		goto exit;
132 
133 	rk_rng_read_regs(pdata->base + CRYPTO_V2_RNG_DOUT_0, data, len);
134 
135 exit:
136 	/* close TRNG */
137 	rk_clrreg(pdata->base + CRYPTO_V2_RNG_CTL, 0xffff);
138 
139 	return retval;
140 }
141 
142 static int rockchip_rng_read(struct udevice *dev, void *data, size_t len)
143 {
144 	unsigned char *buf = data;
145 	unsigned int i;
146 	int ret = -EIO;
147 
148 	struct rk_rng_platdata *pdata = dev_get_priv(dev);
149 
150 	if (!len)
151 		return 0;
152 
153 	if (!pdata->soc_data || !pdata->soc_data->rk_rng_read)
154 		return -EINVAL;
155 
156 	for (i = 0; i < len / RK_HW_RNG_MAX; i++, buf += RK_HW_RNG_MAX) {
157 		ret = pdata->soc_data->rk_rng_read(dev, buf, RK_HW_RNG_MAX);
158 		if (ret)
159 			goto exit;
160 	}
161 
162 	if (len % RK_HW_RNG_MAX)
163 		ret = pdata->soc_data->rk_rng_read(dev, buf,
164 						   len % RK_HW_RNG_MAX);
165 
166 exit:
167 	return ret;
168 }
169 
170 static int rockchip_rng_ofdata_to_platdata(struct udevice *dev)
171 {
172 	struct rk_rng_platdata *pdata = dev_get_priv(dev);
173 
174 	memset(pdata, 0x00, sizeof(*pdata));
175 
176 	pdata->base = (fdt_addr_t)dev_read_addr_ptr(dev);
177 	if (!pdata->base)
178 		return -ENOMEM;
179 
180 	return 0;
181 }
182 
183 static int rockchip_rng_probe(struct udevice *dev)
184 {
185 	struct rk_rng_platdata *pdata = dev_get_priv(dev);
186 
187 	pdata->soc_data = (struct rk_rng_soc_data *)dev_get_driver_data(dev);
188 
189 	return 0;
190 }
191 
192 static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
193 	.rk_rng_read = rk_v1_rng_read,
194 };
195 
196 static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
197 	.rk_rng_read = rk_v2_rng_read,
198 };
199 
200 static const struct dm_rng_ops rockchip_rng_ops = {
201 	.read = rockchip_rng_read,
202 };
203 
204 static const struct udevice_id rockchip_rng_match[] = {
205 	{
206 		.compatible = "rockchip,cryptov1-rng",
207 		.data = (ulong)&rk_rng_v1_soc_data,
208 	},
209 	{
210 		.compatible = "rockchip,cryptov2-rng",
211 		.data = (ulong)&rk_rng_v2_soc_data,
212 	},
213 	{},
214 };
215 
216 U_BOOT_DRIVER(rockchip_rng) = {
217 	.name = "rockchip-rng",
218 	.id = UCLASS_RNG,
219 	.of_match = rockchip_rng_match,
220 	.ops = &rockchip_rng_ops,
221 	.probe = rockchip_rng_probe,
222 	.ofdata_to_platdata = rockchip_rng_ofdata_to_platdata,
223 	.priv_auto_alloc_size = sizeof(struct rk_rng_platdata),
224 };
225