blob: d2479d244ed5ac578d69c24cb519f73545bd18c6 [file] [log] [blame]
Sam Protsenko110f8102024-07-23 13:14:38 -05001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2024 Linaro Ltd.
4 * Author: Sam Protsenko <semen.protsenko@linaro.org>
5 *
6 * Samsung Exynos TRNG driver (True Random Number Generator).
7 */
8
9#include <clk.h>
10#include <dm.h>
11#include <rng.h>
12#include <dm/device.h>
13#include <dm/device_compat.h>
14#include <asm/io.h>
15#include <linux/arm-smccc.h>
16#include <linux/bitops.h>
17#include <linux/delay.h>
18#include <linux/iopoll.h>
19#include <linux/time.h>
20
21#define EXYNOS_TRNG_CLKDIV 0x0
22#define EXYNOS_TRNG_CLKDIV_MASK GENMASK(15, 0)
23#define EXYNOS_TRNG_CLOCK_RATE 500000
24
25#define EXYNOS_TRNG_CTRL 0x20
26#define EXYNOS_TRNG_CTRL_RNGEN BIT(31)
27
28#define EXYNOS_TRNG_POST_CTRL 0x30
29#define EXYNOS_TRNG_ONLINE_CTRL 0x40
30#define EXYNOS_TRNG_ONLINE_STAT 0x44
31#define EXYNOS_TRNG_ONLINE_MAXCHI2 0x48
32#define EXYNOS_TRNG_FIFO_CTRL 0x50
33#define EXYNOS_TRNG_FIFO_0 0x80
34#define EXYNOS_TRNG_FIFO_1 0x84
35#define EXYNOS_TRNG_FIFO_2 0x88
36#define EXYNOS_TRNG_FIFO_3 0x8c
37#define EXYNOS_TRNG_FIFO_4 0x90
38#define EXYNOS_TRNG_FIFO_5 0x94
39#define EXYNOS_TRNG_FIFO_6 0x98
40#define EXYNOS_TRNG_FIFO_7 0x9c
41#define EXYNOS_TRNG_FIFO_LEN 8
42#define EXYNOS_TRNG_FIFO_TIMEOUT (1 * USEC_PER_SEC)
43
44#define EXYNOS_SMC_CALL_VAL(func_num) \
45 ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
46 ARM_SMCCC_SMC_32, \
47 ARM_SMCCC_OWNER_SIP, \
48 func_num)
49
50/* SMC command for DTRNG access */
51#define SMC_CMD_RANDOM EXYNOS_SMC_CALL_VAL(0x1012)
52
53/* SMC_CMD_RANDOM: arguments */
54#define HWRNG_INIT 0x0
55#define HWRNG_EXIT 0x1
56#define HWRNG_GET_DATA 0x2
57
58/* SMC_CMD_RANDOM: return values */
59#define HWRNG_RET_OK 0x0
60#define HWRNG_RET_RETRY_ERROR 0x2
61
62#define HWRNG_MAX_TRIES 100
63
64/**
65 * struct exynos_trng_variant - Chip specific data
66 *
67 * @smc: Set "true" if TRNG block has to be accessed via SMC calls
68 * @init: (Optional) TRNG initialization function to call on probe
69 * @exit: (Optional) TRNG deinitialization function to call on remove
70 * @read: Function to read the random data from TRNG block
71 */
72struct exynos_trng_variant {
73 bool smc;
74 int (*init)(struct udevice *dev);
75 void (*exit)(struct udevice *dev);
76 int (*read)(struct udevice *dev, void *data, size_t len);
77};
78
79/**
80 * struct exynos_trng_priv - Driver's private data
81 *
82 * @base: Base address of MMIO registers of TRNG block
83 * @clk: Operating clock (needed for TRNG block functioning)
84 * @pclk: Bus clock (needed for interfacing the TRNG block registers)
85 * @data: Chip specific data
86 */
87struct exynos_trng_priv {
88 void __iomem *base;
89 struct clk *clk;
90 struct clk *pclk;
91 const struct exynos_trng_variant *data;
92};
93
94static int exynos_trng_read_reg(struct udevice *dev, void *data, size_t len)
95{
96 struct exynos_trng_priv *trng = dev_get_priv(dev);
97 int val;
98
99 len = min_t(size_t, len, EXYNOS_TRNG_FIFO_LEN * 4);
100 writel_relaxed(len * 8, trng->base + EXYNOS_TRNG_FIFO_CTRL);
101 val = readl_poll_timeout(trng->base + EXYNOS_TRNG_FIFO_CTRL, val,
102 val == 0, EXYNOS_TRNG_FIFO_TIMEOUT);
103 if (val < 0)
104 return val;
105
106 memcpy_fromio(data, trng->base + EXYNOS_TRNG_FIFO_0, len);
107
108 return 0;
109}
110
111static int exynos_trng_read_smc(struct udevice *dev, void *data, size_t len)
112{
113 struct arm_smccc_res res;
114 unsigned int copied = 0;
115 u32 *buf = data;
116 int tries = 0;
117
118 while (copied < len) {
119 arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
120 &res);
121 switch (res.a0) {
122 case HWRNG_RET_OK:
123 *buf++ = res.a2;
124 *buf++ = res.a3;
125 copied += 8;
126 tries = 0;
127 break;
128 case HWRNG_RET_RETRY_ERROR:
129 if (++tries >= HWRNG_MAX_TRIES)
130 return -EIO;
131 udelay(10);
132 break;
133 default:
134 return -EIO;
135 }
136 }
137
138 return 0;
139}
140
141static int exynos_trng_init_reg(struct udevice *dev)
142{
143 const u32 max_div = EXYNOS_TRNG_CLKDIV_MASK;
144 struct exynos_trng_priv *trng = dev_get_priv(dev);
145 unsigned long sss_rate;
146 u32 div;
147
148 sss_rate = clk_get_rate(trng->clk);
149
150 /*
151 * For most TRNG circuits the clock frequency of under 500 kHz is safe.
152 * The clock divider should be an even number.
153 */
154 div = sss_rate / EXYNOS_TRNG_CLOCK_RATE;
155 div -= div % 2; /* make sure it's even */
156 if (div > max_div) {
157 dev_err(dev, "Clock divider too large: %u", div);
158 return -ERANGE;
159 }
160 writel_relaxed(div, trng->base + EXYNOS_TRNG_CLKDIV);
161
162 /* Enable the generator */
163 writel_relaxed(EXYNOS_TRNG_CTRL_RNGEN, trng->base + EXYNOS_TRNG_CTRL);
164
165 /* Disable post-processing */
166 writel_relaxed(0, trng->base + EXYNOS_TRNG_POST_CTRL);
167
168 return 0;
169}
170
171static int exynos_trng_init_smc(struct udevice *dev)
172{
173 struct arm_smccc_res res;
174 int ret = 0;
175
176 arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0, &res);
177 if (res.a0 != HWRNG_RET_OK) {
178 dev_err(dev, "SMC command for TRNG init failed (%d)\n",
179 (int)res.a0);
180 ret = -EIO;
181 }
182 if ((int)res.a0 == -1)
183 dev_info(dev, "Make sure LDFW is loaded\n");
184
185 return ret;
186}
187
188static void exynos_trng_exit_smc(struct udevice *dev)
189{
190 struct arm_smccc_res res;
191
192 arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0, &res);
193}
194
195static int exynos_trng_read(struct udevice *dev, void *data, size_t len)
196{
197 struct exynos_trng_priv *trng = dev_get_priv(dev);
198
199 return trng->data->read(dev, data, len);
200}
201
202static int exynos_trng_of_to_plat(struct udevice *dev)
203{
204 struct exynos_trng_priv *trng = dev_get_priv(dev);
205
206 trng->data = (struct exynos_trng_variant *)dev_get_driver_data(dev);
207 if (!trng->data->smc) {
208 trng->base = dev_read_addr_ptr(dev);
209 if (!trng->base)
210 return -EINVAL;
211 }
212
213 trng->clk = devm_clk_get(dev, "secss");
214 if (IS_ERR(trng->clk))
215 return PTR_ERR(trng->clk);
216
217 trng->pclk = devm_clk_get_optional(dev, "pclk");
218 if (IS_ERR(trng->pclk))
219 return PTR_ERR(trng->pclk);
220
221 return 0;
222}
223
224static int exynos_trng_probe(struct udevice *dev)
225{
226 struct exynos_trng_priv *trng = dev_get_priv(dev);
227 int ret;
228
229 ret = clk_enable(trng->pclk);
230 if (ret)
231 return ret;
232
233 ret = clk_enable(trng->clk);
234 if (ret)
235 return ret;
236
237 if (trng->data->init)
238 ret = trng->data->init(dev);
239
240 return ret;
241}
242
243static int exynos_trng_remove(struct udevice *dev)
244{
245 struct exynos_trng_priv *trng = dev_get_priv(dev);
246
247 if (trng->data->exit)
248 trng->data->exit(dev);
249
250 /* Keep SSS clocks enabled, they are needed for EL3_MON and kernel */
251
252 return 0;
253}
254
255static const struct dm_rng_ops exynos_trng_ops = {
256 .read = exynos_trng_read,
257};
258
259static const struct exynos_trng_variant exynos5250_trng_data = {
260 .init = exynos_trng_init_reg,
261 .read = exynos_trng_read_reg,
262};
263
264static const struct exynos_trng_variant exynos850_trng_data = {
265 .smc = true,
266 .init = exynos_trng_init_smc,
267 .exit = exynos_trng_exit_smc,
268 .read = exynos_trng_read_smc,
269};
270
271static const struct udevice_id exynos_trng_match[] = {
272 {
273 .compatible = "samsung,exynos5250-trng",
274 .data = (ulong)&exynos5250_trng_data,
275 }, {
276 .compatible = "samsung,exynos850-trng",
277 .data = (ulong)&exynos850_trng_data,
278 },
279 { },
280};
281
282U_BOOT_DRIVER(exynos_trng) = {
283 .name = "exynos-trng",
284 .id = UCLASS_RNG,
285 .of_match = exynos_trng_match,
286 .of_to_plat = exynos_trng_of_to_plat,
287 .probe = exynos_trng_probe,
288 .remove = exynos_trng_remove,
289 .ops = &exynos_trng_ops,
290 .priv_auto = sizeof(struct exynos_trng_priv),
291};