blob: a9dc43f8da600092dbf2b454dee3ff621fee204a [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) {
190 data32 = mmio_read_32(stm32_rng.base + RNG_DR);
191 count--;
192
193 memcpy(buf, &data32, MIN(len, sizeof(uint32_t)));
194 buf += MIN(len, sizeof(uint32_t));
195 len -= MIN(len, sizeof(uint32_t));
196
197 if (count == 0U) {
198 break;
199 }
200 }
201 }
202
203bail:
204 if (rc != 0) {
205 memset(out, 0, buf - out);
206 }
207
208 return rc;
209}
210
211/*
212 * stm32_rng_init: Initialize rng from DT
213 * return 0 on success, negative value on failure
214 */
215int stm32_rng_init(void)
216{
217 void *fdt;
218 struct dt_node_info dt_rng;
219 int node;
220
221 if (stm32_rng.base != 0U) {
222 /* Driver is already initialized */
223 return 0;
224 }
225
226 if (fdt_get_address(&fdt) == 0) {
227 panic();
228 }
229
230 node = dt_get_node(&dt_rng, -1, DT_RNG_COMPAT);
231 if (node < 0) {
232 return 0;
233 }
234
235 if (dt_rng.status == DT_DISABLED) {
236 return 0;
237 }
238
239 assert(dt_rng.base != 0U);
240
241 stm32_rng.base = dt_rng.base;
242
243 if (dt_rng.clock < 0) {
244 panic();
245 }
246
247 stm32_rng.clock = (unsigned long)dt_rng.clock;
248 clk_enable(stm32_rng.clock);
249
250 if (dt_rng.reset >= 0) {
251 int ret;
252
253 ret = stm32mp_reset_assert((unsigned long)dt_rng.reset,
254 TIMEOUT_US_1MS);
255 if (ret != 0) {
256 panic();
257 }
258
259 udelay(20);
260
261 ret = stm32mp_reset_deassert((unsigned long)dt_rng.reset,
262 TIMEOUT_US_1MS);
263 if (ret != 0) {
264 panic();
265 }
266 }
267
268 return stm32_rng_enable();
269}