blob: 1342fd4549f49ab3b27dec271710924b233df0cc [file] [log] [blame]
Yann Gautierf08879f2019-04-18 14:47:35 +02001/*
2 * Copyright (c) 2022, STMicroelectronics - All Rights Reserved
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <stdbool.h>
10
11#include <arch_helpers.h>
12#include <drivers/clk.h>
13#include <drivers/delay_timer.h>
14#include <drivers/st/stm32_rng.h>
15#include <drivers/st/stm32mp_reset.h>
16#include <lib/mmio.h>
17#include <libfdt.h>
18
19#include <platform_def.h>
20
21#if STM32_RNG_VER == 2
22#define DT_RNG_COMPAT "st,stm32-rng"
23#endif
24#if STM32_RNG_VER == 4
25#define DT_RNG_COMPAT "st,stm32mp13-rng"
26#endif
27#define RNG_CR 0x00U
28#define RNG_SR 0x04U
29#define RNG_DR 0x08U
30
31#define RNG_CR_RNGEN BIT(2)
32#define RNG_CR_IE BIT(3)
33#define RNG_CR_CED BIT(5)
34#define RNG_CR_CLKDIV GENMASK(19, 16)
35#define RNG_CR_CLKDIV_SHIFT 16U
36#define RNG_CR_CONDRST BIT(30)
37
38#define RNG_SR_DRDY BIT(0)
39#define RNG_SR_CECS BIT(1)
40#define RNG_SR_SECS BIT(2)
41#define RNG_SR_CEIS BIT(5)
42#define RNG_SR_SEIS BIT(6)
43
44#define RNG_TIMEOUT_US 100000U
45#define RNG_TIMEOUT_STEP_US 10U
46
47#define TIMEOUT_US_1MS 1000U
48
49#define RNG_NIST_CONFIG_A 0x00F40F00U
50#define RNG_NIST_CONFIG_B 0x01801000U
51#define RNG_NIST_CONFIG_C 0x00F00D00U
52#define RNG_NIST_CONFIG_MASK GENMASK(25, 8)
53
54#define RNG_MAX_NOISE_CLK_FREQ 48000000U
55
56struct stm32_rng_instance {
57 uintptr_t base;
58 unsigned long clock;
59};
60
61static struct stm32_rng_instance stm32_rng;
62
63static void seed_error_recovery(void)
64{
65 uint8_t i __maybe_unused;
66
67 /* Recommended by the SoC reference manual */
68 mmio_clrbits_32(stm32_rng.base + RNG_SR, RNG_SR_SEIS);
69 dmbsy();
70
71#if STM32_RNG_VER == 2
72 /* No Auto-reset on version 2, need to clean FIFO */
73 for (i = 12U; i != 0U; i--) {
74 (void)mmio_read_32(stm32_rng.base + RNG_DR);
75 }
76
77 dmbsy();
78#endif
79
80 if ((mmio_read_32(stm32_rng.base + RNG_SR) & RNG_SR_SEIS) != 0U) {
81 ERROR("RNG noise\n");
82 panic();
83 }
84}
85
86static uint32_t stm32_rng_clock_freq_restrain(void)
87{
88 unsigned long clock_rate;
89 uint32_t clock_div = 0U;
90
91 clock_rate = clk_get_rate(stm32_rng.clock);
92
93 /*
94 * Get the exponent to apply on the CLKDIV field in RNG_CR register
95 * No need to handle the case when clock-div > 0xF as it is physically
96 * impossible
97 */
98 while ((clock_rate >> clock_div) > RNG_MAX_NOISE_CLK_FREQ) {
99 clock_div++;
100 }
101
102 VERBOSE("RNG clk rate : %lu\n", clk_get_rate(stm32_rng.clock) >> clock_div);
103
104 return clock_div;
105}
106
107static int stm32_rng_enable(void)
108{
109 uint32_t sr;
110 uint64_t timeout;
111 uint32_t clock_div __maybe_unused;
112
113#if STM32_RNG_VER == 2
114 mmio_write_32(stm32_rng.base + RNG_CR, RNG_CR_RNGEN | RNG_CR_CED);
115#endif
116#if STM32_RNG_VER == 4
117 /* Reset internal block and disable CED bit */
118 clock_div = stm32_rng_clock_freq_restrain();
119
120 /* Update configuration fields */
121 mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_NIST_CONFIG_MASK,
122 RNG_NIST_CONFIG_A | RNG_CR_CONDRST | RNG_CR_CED);
123
124 mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_CR_CLKDIV,
125 (clock_div << RNG_CR_CLKDIV_SHIFT));
126
127 mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_CR_CONDRST, RNG_CR_RNGEN);
128#endif
129 timeout = timeout_init_us(RNG_TIMEOUT_US);
130 sr = mmio_read_32(stm32_rng.base + RNG_SR);
131 while ((sr & RNG_SR_DRDY) == 0U) {
132 if (timeout_elapsed(timeout)) {
133 WARN("Timeout waiting\n");
134 return -ETIMEDOUT;
135 }
136
137 if ((sr & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) {
138 seed_error_recovery();
139 timeout = timeout_init_us(RNG_TIMEOUT_US);
140 }
141
142 udelay(RNG_TIMEOUT_STEP_US);
143 sr = mmio_read_32(stm32_rng.base + RNG_SR);
144 }
145
146 VERBOSE("Init RNG done\n");
147
148 return 0;
149}
150
151/*
152 * stm32_rng_read - Read a number of random bytes from RNG
153 * out: pointer to the output buffer
154 * size: number of bytes to be read
155 * Return 0 on success, non-0 on failure
156 */
157int stm32_rng_read(uint8_t *out, uint32_t size)
158{
159 uint8_t *buf = out;
160 size_t len = size;
161 int nb_tries;
162 uint32_t data32;
163 int rc = 0;
164 unsigned int count;
165
166 if (stm32_rng.base == 0U) {
167 return -EPERM;
168 }
169
170 while (len != 0U) {
171 nb_tries = RNG_TIMEOUT_US / RNG_TIMEOUT_STEP_US;
172 do {
173 uint32_t status = mmio_read_32(stm32_rng.base + RNG_SR);
174
175 if ((status & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) {
176 seed_error_recovery();
177 }
178
179 udelay(RNG_TIMEOUT_STEP_US);
180 nb_tries--;
181 if (nb_tries == 0) {
182 rc = -ETIMEDOUT;
183 goto bail;
184 }
185 } while ((mmio_read_32(stm32_rng.base + RNG_SR) &
186 RNG_SR_DRDY) == 0U);
187
188 count = 4U;
189 while (len != 0U) {
Gatien Chevallier58206ec2023-05-26 15:49:03 +0200190 if ((mmio_read_32(stm32_rng.base + RNG_SR) & RNG_SR_DRDY) == 0U) {
191 break;
192 }
193
Yann Gautierf08879f2019-04-18 14:47:35 +0200194 data32 = mmio_read_32(stm32_rng.base + RNG_DR);
195 count--;
196
197 memcpy(buf, &data32, MIN(len, sizeof(uint32_t)));
198 buf += MIN(len, sizeof(uint32_t));
199 len -= MIN(len, sizeof(uint32_t));
200
201 if (count == 0U) {
202 break;
203 }
204 }
205 }
206
207bail:
208 if (rc != 0) {
209 memset(out, 0, buf - out);
210 }
211
212 return rc;
213}
214
215/*
216 * stm32_rng_init: Initialize rng from DT
217 * return 0 on success, negative value on failure
218 */
219int stm32_rng_init(void)
220{
221 void *fdt;
222 struct dt_node_info dt_rng;
223 int node;
224
225 if (stm32_rng.base != 0U) {
226 /* Driver is already initialized */
227 return 0;
228 }
229
230 if (fdt_get_address(&fdt) == 0) {
231 panic();
232 }
233
234 node = dt_get_node(&dt_rng, -1, DT_RNG_COMPAT);
235 if (node < 0) {
236 return 0;
237 }
238
239 if (dt_rng.status == DT_DISABLED) {
240 return 0;
241 }
242
243 assert(dt_rng.base != 0U);
244
245 stm32_rng.base = dt_rng.base;
246
247 if (dt_rng.clock < 0) {
248 panic();
249 }
250
251 stm32_rng.clock = (unsigned long)dt_rng.clock;
252 clk_enable(stm32_rng.clock);
253
254 if (dt_rng.reset >= 0) {
255 int ret;
256
257 ret = stm32mp_reset_assert((unsigned long)dt_rng.reset,
258 TIMEOUT_US_1MS);
259 if (ret != 0) {
260 panic();
261 }
262
263 udelay(20);
264
265 ret = stm32mp_reset_deassert((unsigned long)dt_rng.reset,
266 TIMEOUT_US_1MS);
267 if (ret != 0) {
268 panic();
269 }
270 }
271
272 return stm32_rng_enable();
273}