blob: 44e8a46075209312779ed5e31df80c0d3f1bd3d7 [file] [log] [blame]
Sughosh Ganucd9a2f92019-12-28 23:58:29 +05301// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019, Linaro Limited
4 */
5
Heinrich Schuchardtd909bab2020-09-17 16:49:02 +02006#define LOG_CATEGORY UCLASS_RNG
7
Sughosh Ganucd9a2f92019-12-28 23:58:29 +05308#include <clk.h>
9#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053011#include <reset.h>
12#include <rng.h>
Heinrich Schuchardt10a45cc2024-02-13 00:44:47 +010013#include <asm/io.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060014#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060015#include <linux/delay.h>
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053016#include <linux/iopoll.h>
17#include <linux/kernel.h>
18
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020019#define RNG_CR 0x00
20#define RNG_CR_RNGEN BIT(2)
21#define RNG_CR_CED BIT(5)
Gatien Chevallier5e65d462023-09-19 17:27:58 +020022#define RNG_CR_CONFIG1 GENMASK(11, 8)
23#define RNG_CR_NISTC BIT(12)
24#define RNG_CR_CONFIG2 GENMASK(15, 13)
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020025#define RNG_CR_CLKDIV_SHIFT 16
Gatien Chevallier5e65d462023-09-19 17:27:58 +020026#define RNG_CR_CLKDIV GENMASK(19, 16)
27#define RNG_CR_CONFIG3 GENMASK(25, 20)
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020028#define RNG_CR_CONDRST BIT(30)
Gatien Chevallier5e65d462023-09-19 17:27:58 +020029#define RNG_CR_ENTROPY_SRC_MASK (RNG_CR_CONFIG1 | RNG_CR_NISTC | RNG_CR_CONFIG2 | RNG_CR_CONFIG3)
30#define RNG_CR_CONFIG_MASK (RNG_CR_ENTROPY_SRC_MASK | RNG_CR_CED | RNG_CR_CLKDIV)
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053031
Lionel Debieve71f44212022-06-30 10:20:15 +020032#define RNG_SR 0x04
33#define RNG_SR_SEIS BIT(6)
34#define RNG_SR_CEIS BIT(5)
35#define RNG_SR_SECS BIT(2)
36#define RNG_SR_DRDY BIT(0)
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053037
Lionel Debieve71f44212022-06-30 10:20:15 +020038#define RNG_DR 0x08
39
Gatien Chevallier5e65d462023-09-19 17:27:58 +020040#define RNG_NSCR 0x0C
41#define RNG_NSCR_MASK GENMASK(17, 0)
42
43#define RNG_HTCR 0x10
44
Gatien Chevallier278ed192023-09-19 17:27:57 +020045#define RNG_NB_RECOVER_TRIES 3
46
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020047/*
48 * struct stm32_rng_data - RNG compat data
49 *
50 * @max_clock_rate: Max RNG clock frequency, in Hertz
Gatien Chevallier5e65d462023-09-19 17:27:58 +020051 * @cr: Entropy source configuration
52 * @nscr: Noice sources control configuration
53 * @htcr: Health tests configuration
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020054 * @has_cond_reset: True if conditionnal reset is supported
55 *
56 */
Lionel Debieve71f44212022-06-30 10:20:15 +020057struct stm32_rng_data {
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +020058 uint max_clock_rate;
Gatien Chevallier5e65d462023-09-19 17:27:58 +020059 u32 cr;
60 u32 nscr;
61 u32 htcr;
Lionel Debieve71f44212022-06-30 10:20:15 +020062 bool has_cond_reset;
63};
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053064
Simon Glassb75b15b2020-12-03 16:55:23 -070065struct stm32_rng_plat {
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053066 fdt_addr_t base;
67 struct clk clk;
68 struct reset_ctl rst;
Lionel Debieve71f44212022-06-30 10:20:15 +020069 const struct stm32_rng_data *data;
Gatien Chevalliere2acc952023-09-19 17:27:55 +020070 bool ced;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +053071};
72
Gatien Chevallier278ed192023-09-19 17:27:57 +020073/*
74 * Extracts from the STM32 RNG specification when RNG supports CONDRST.
75 *
76 * When a noise source (or seed) error occurs, the RNG stops generating
Michal Simekcc046dc2024-04-16 08:55:19 +020077 * random numbers and sets to "1" both SEIS and SECS bits to indicate
Gatien Chevallier278ed192023-09-19 17:27:57 +020078 * that a seed error occurred. (...)
79 *
80 * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield
81 * description for details). This step is needed only if SECS is set.
82 * Indeed, when SEIS is set and SECS is cleared it means RNG performed
83 * the reset automatically (auto-reset).
84 * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST
85 * to be cleared in the RNG_CR register, then confirm that SEIS is
86 * cleared in the RNG_SR register. Otherwise just clear SEIS bit in
87 * the RNG_SR register.
88 * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be
89 * cleared by RNG. The random number generation is now back to normal.
90 */
91static int stm32_rng_conceal_seed_error_cond_reset(struct stm32_rng_plat *pdata)
92{
93 u32 sr = readl_relaxed(pdata->base + RNG_SR);
94 u32 cr = readl_relaxed(pdata->base + RNG_CR);
95 int err;
96
97 if (sr & RNG_SR_SECS) {
98 /* Conceal by resetting the subsystem (step 1.) */
99 writel_relaxed(cr | RNG_CR_CONDRST, pdata->base + RNG_CR);
100 writel_relaxed(cr & ~RNG_CR_CONDRST, pdata->base + RNG_CR);
101 } else {
102 /* RNG auto-reset (step 2.) */
103 writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR);
104 return 0;
105 }
106
107 err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_CR_CONDRST), 100000);
108 if (err) {
109 log_err("%s: timeout %x\n", __func__, sr);
110 return err;
111 }
112
113 /* Check SEIS is cleared (step 2.) */
114 if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS)
115 return -EINVAL;
116
117 err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_SR_SECS), 100000);
118 if (err) {
119 log_err("%s: timeout %x\n", __func__, sr);
120 return err;
121 }
122
123 return 0;
124}
125
126/*
127 * Extracts from the STM32 RNG specification, when CONDRST is not supported
128 *
129 * When a noise source (or seed) error occurs, the RNG stops generating
Michal Simekcc046dc2024-04-16 08:55:19 +0200130 * random numbers and sets to "1" both SEIS and SECS bits to indicate
Gatien Chevallier278ed192023-09-19 17:27:57 +0200131 * that a seed error occurred. (...)
132 *
133 * The following sequence shall be used to fully recover from a seed
134 * error after the RNG initialization:
Michal Simekcc046dc2024-04-16 08:55:19 +0200135 * 1. Clear the SEIS bit by writing it to "0".
Gatien Chevallier278ed192023-09-19 17:27:57 +0200136 * 2. Read out 12 words from the RNG_DR register, and discard each of
137 * them in order to clean the pipeline.
138 * 3. Confirm that SEIS is still cleared. Random number generation is
139 * back to normal.
140 */
141static int stm32_rng_conceal_seed_error_sw_reset(struct stm32_rng_plat *pdata)
142{
143 uint i = 0;
144 u32 sr = readl_relaxed(pdata->base + RNG_SR);
145
146 writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR);
147
148 for (i = 12; i != 0; i--)
149 (void)readl_relaxed(pdata->base + RNG_DR);
150
151 if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS)
152 return -EINVAL;
153
154 return 0;
155}
156
157static int stm32_rng_conceal_seed_error(struct stm32_rng_plat *pdata)
158{
159 log_debug("Concealing RNG seed error\n");
160
161 if (pdata->data->has_cond_reset)
162 return stm32_rng_conceal_seed_error_cond_reset(pdata);
163 else
164 return stm32_rng_conceal_seed_error_sw_reset(pdata);
165};
166
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530167static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
168{
Gatien Chevallier278ed192023-09-19 17:27:57 +0200169 int retval;
170 u32 sr, reg;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530171 size_t increment;
Simon Glassb75b15b2020-12-03 16:55:23 -0700172 struct stm32_rng_plat *pdata = dev_get_plat(dev);
Gatien Chevallier278ed192023-09-19 17:27:57 +0200173 uint tries = 0;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530174
175 while (len > 0) {
176 retval = readl_poll_timeout(pdata->base + RNG_SR, sr,
Gatien Chevallier278ed192023-09-19 17:27:57 +0200177 sr, 10000);
178 if (retval) {
179 log_err("%s: Timeout RNG no data", __func__);
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530180 return retval;
Gatien Chevallier278ed192023-09-19 17:27:57 +0200181 }
182
183 if (sr != RNG_SR_DRDY) {
184 if (sr & RNG_SR_SEIS) {
185 retval = stm32_rng_conceal_seed_error(pdata);
186 tries++;
187 if (retval || tries > RNG_NB_RECOVER_TRIES) {
188 log_err("%s: Couldn't recover from seed error", __func__);
189 return -ENOTRECOVERABLE;
190 }
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530191
Gatien Chevallier278ed192023-09-19 17:27:57 +0200192 /* Start again */
193 continue;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530194 }
Gatien Chevallier278ed192023-09-19 17:27:57 +0200195
196 if (sr & RNG_SR_CEIS) {
197 log_info("RNG clock too slow");
198 writel_relaxed(0, pdata->base + RNG_SR);
199 }
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530200 }
201
202 /*
203 * Once the DRDY bit is set, the RNG_DR register can
Gatien Chevallier278ed192023-09-19 17:27:57 +0200204 * be read up to four consecutive times.
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530205 */
Gatien Chevallier278ed192023-09-19 17:27:57 +0200206 reg = readl(pdata->base + RNG_DR);
207 /* Late seed error case: DR being 0 is an error status */
208 if (!reg) {
209 retval = stm32_rng_conceal_seed_error(pdata);
210 tries++;
211
212 if (retval || tries > RNG_NB_RECOVER_TRIES) {
213 log_err("%s: Couldn't recover from seed error", __func__);
214 return -ENOTRECOVERABLE;
215 }
216
217 /* Start again */
218 continue;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530219 }
Gatien Chevallier278ed192023-09-19 17:27:57 +0200220
221 increment = min(len, sizeof(u32));
222 memcpy(data, &reg, increment);
223 data += increment;
224 len -= increment;
225
226 tries = 0;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530227 }
228
229 return 0;
230}
231
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +0200232static uint stm32_rng_clock_freq_restrain(struct stm32_rng_plat *pdata)
233{
234 ulong clock_rate = 0;
235 uint clock_div = 0;
236
237 clock_rate = clk_get_rate(&pdata->clk);
238
239 /*
240 * Get the exponent to apply on the CLKDIV field in RNG_CR register.
241 * No need to handle the case when clock-div > 0xF as it is physically
242 * impossible.
243 */
244 while ((clock_rate >> clock_div) > pdata->data->max_clock_rate)
245 clock_div++;
246
247 log_debug("RNG clk rate : %lu\n", clk_get_rate(&pdata->clk) >> clock_div);
248
249 return clock_div;
250}
251
Simon Glassb75b15b2020-12-03 16:55:23 -0700252static int stm32_rng_init(struct stm32_rng_plat *pdata)
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530253{
254 int err;
Lionel Debieve71f44212022-06-30 10:20:15 +0200255 u32 cr, sr;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530256
257 err = clk_enable(&pdata->clk);
258 if (err)
259 return err;
260
Lionel Debieve71f44212022-06-30 10:20:15 +0200261 cr = readl(pdata->base + RNG_CR);
262
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200263 /*
264 * Keep default RNG configuration if none was specified, that is when conf.cr is set to 0.
265 */
266 if (pdata->data->has_cond_reset && pdata->data->cr) {
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +0200267 uint clock_div = stm32_rng_clock_freq_restrain(pdata);
268
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200269 cr &= ~RNG_CR_CONFIG_MASK;
270 cr |= RNG_CR_CONDRST | (pdata->data->cr & RNG_CR_ENTROPY_SRC_MASK) |
271 (clock_div << RNG_CR_CLKDIV_SHIFT);
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200272 if (pdata->ced)
273 cr &= ~RNG_CR_CED;
274 else
275 cr |= RNG_CR_CED;
Lionel Debieve71f44212022-06-30 10:20:15 +0200276 writel(cr, pdata->base + RNG_CR);
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200277
278 /* Health tests and noise control registers */
279 writel_relaxed(pdata->data->htcr, pdata->base + RNG_HTCR);
280 writel_relaxed(pdata->data->nscr & RNG_NSCR_MASK, pdata->base + RNG_NSCR);
281
Lionel Debieve71f44212022-06-30 10:20:15 +0200282 cr &= ~RNG_CR_CONDRST;
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200283 cr |= RNG_CR_RNGEN;
Lionel Debieve71f44212022-06-30 10:20:15 +0200284 writel(cr, pdata->base + RNG_CR);
285 err = readl_poll_timeout(pdata->base + RNG_CR, cr,
286 (!(cr & RNG_CR_CONDRST)), 10000);
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200287 if (err) {
288 log_err("%s: Timeout!", __func__);
Lionel Debieve71f44212022-06-30 10:20:15 +0200289 return err;
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200290 }
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200291 } else {
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200292 if (pdata->data->has_cond_reset)
293 cr |= RNG_CR_CONDRST;
294
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200295 if (pdata->ced)
296 cr &= ~RNG_CR_CED;
297 else
298 cr |= RNG_CR_CED;
299
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200300 writel(cr, pdata->base + RNG_CR);
301
302 if (pdata->data->has_cond_reset)
303 cr &= ~RNG_CR_CONDRST;
304
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200305 cr |= RNG_CR_RNGEN;
306
307 writel(cr, pdata->base + RNG_CR);
Lionel Debieve71f44212022-06-30 10:20:15 +0200308 }
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530309
310 /* clear error indicators */
311 writel(0, pdata->base + RNG_SR);
312
Lionel Debieve71f44212022-06-30 10:20:15 +0200313 err = readl_poll_timeout(pdata->base + RNG_SR, sr,
314 sr & RNG_SR_DRDY, 10000);
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200315 if (err)
316 log_err("%s: Timeout!", __func__);
317
Lionel Debieve71f44212022-06-30 10:20:15 +0200318 return err;
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530319}
320
Simon Glassb75b15b2020-12-03 16:55:23 -0700321static int stm32_rng_cleanup(struct stm32_rng_plat *pdata)
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530322{
323 writel(0, pdata->base + RNG_CR);
324
325 return clk_disable(&pdata->clk);
326}
327
328static int stm32_rng_probe(struct udevice *dev)
329{
Simon Glassb75b15b2020-12-03 16:55:23 -0700330 struct stm32_rng_plat *pdata = dev_get_plat(dev);
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530331
Lionel Debieve71f44212022-06-30 10:20:15 +0200332 pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev);
333
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530334 reset_assert(&pdata->rst);
335 udelay(20);
336 reset_deassert(&pdata->rst);
337
338 return stm32_rng_init(pdata);
339}
340
341static int stm32_rng_remove(struct udevice *dev)
342{
Simon Glassb75b15b2020-12-03 16:55:23 -0700343 struct stm32_rng_plat *pdata = dev_get_plat(dev);
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530344
345 return stm32_rng_cleanup(pdata);
346}
347
Simon Glassaad29ae2020-12-03 16:55:21 -0700348static int stm32_rng_of_to_plat(struct udevice *dev)
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530349{
Simon Glassb75b15b2020-12-03 16:55:23 -0700350 struct stm32_rng_plat *pdata = dev_get_plat(dev);
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530351 int err;
352
353 pdata->base = dev_read_addr(dev);
354 if (!pdata->base)
355 return -ENOMEM;
356
357 err = clk_get_by_index(dev, 0, &pdata->clk);
358 if (err)
359 return err;
360
361 err = reset_get_by_index(dev, 0, &pdata->rst);
362 if (err)
363 return err;
364
Gatien Chevalliere2acc952023-09-19 17:27:55 +0200365 pdata->ced = dev_read_bool(dev, "clock-error-detect");
366
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530367 return 0;
368}
369
370static const struct dm_rng_ops stm32_rng_ops = {
371 .read = stm32_rng_read,
372};
373
Lionel Debieve71f44212022-06-30 10:20:15 +0200374static const struct stm32_rng_data stm32mp13_rng_data = {
375 .has_cond_reset = true,
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +0200376 .max_clock_rate = 48000000,
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200377 .htcr = 0x969D,
378 .nscr = 0x2B5BB,
379 .cr = 0xF00D00,
Lionel Debieve71f44212022-06-30 10:20:15 +0200380};
381
382static const struct stm32_rng_data stm32_rng_data = {
383 .has_cond_reset = false,
Gatien Chevallier4f1f9e72023-09-19 17:27:56 +0200384 .max_clock_rate = 3000000,
Gatien Chevallier5e65d462023-09-19 17:27:58 +0200385 /* Not supported */
386 .htcr = 0,
387 .nscr = 0,
388 .cr = 0,
Lionel Debieve71f44212022-06-30 10:20:15 +0200389};
390
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530391static const struct udevice_id stm32_rng_match[] = {
Lionel Debieve71f44212022-06-30 10:20:15 +0200392 {.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data},
393 {.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data},
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530394 {},
395};
396
397U_BOOT_DRIVER(stm32_rng) = {
398 .name = "stm32-rng",
399 .id = UCLASS_RNG,
400 .of_match = stm32_rng_match,
401 .ops = &stm32_rng_ops,
402 .probe = stm32_rng_probe,
403 .remove = stm32_rng_remove,
Simon Glassb75b15b2020-12-03 16:55:23 -0700404 .plat_auto = sizeof(struct stm32_rng_plat),
Simon Glassaad29ae2020-12-03 16:55:21 -0700405 .of_to_plat = stm32_rng_of_to_plat,
Sughosh Ganucd9a2f92019-12-28 23:58:29 +0530406};