xref: /optee_os/core/drivers/stm32_rng.c (revision 3e64c635856ea5a1aba1293429a440809d97edb2)
1f3c22059SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2f3c22059SEtienne Carriere /*
3ea8ba295SGatien Chevallier  * Copyright (c) 2018-2023, STMicroelectronics
4f3c22059SEtienne Carriere  */
5f3c22059SEtienne Carriere 
6f3c22059SEtienne Carriere #include <assert.h>
7d7a1a7d2SEtienne Carriere #include <drivers/clk.h>
8d7a1a7d2SEtienne Carriere #include <drivers/clk_dt.h>
9ea8ba295SGatien Chevallier #include <drivers/rstctrl.h>
10f3c22059SEtienne Carriere #include <drivers/stm32_rng.h>
11f3c22059SEtienne Carriere #include <io.h>
12f3c22059SEtienne Carriere #include <kernel/delay.h>
13a2fc83d1SJerome Forissier #include <kernel/dt.h>
14ea8ba295SGatien Chevallier #include <kernel/dt_driver.h>
1565401337SJens Wiklander #include <kernel/boot.h>
16f3c22059SEtienne Carriere #include <kernel/panic.h>
1729893549SGatien Chevallier #include <kernel/pm.h>
1865b5ada4SMarouene Boubakri #include <kernel/thread.h>
19a2fc83d1SJerome Forissier #include <libfdt.h>
20f3c22059SEtienne Carriere #include <mm/core_memprot.h>
21097f329aSEtienne Carriere #include <rng_support.h>
22f3c22059SEtienne Carriere #include <stdbool.h>
23f3c22059SEtienne Carriere #include <stm32_util.h>
24f3c22059SEtienne Carriere #include <string.h>
25cd451498SEtienne Carriere #include <tee/tee_cryp_utl.h>
26f3c22059SEtienne Carriere 
270817aa6fSGatien Chevallier #define RNG_CR			U(0x00)
280817aa6fSGatien Chevallier #define RNG_SR			U(0x04)
290817aa6fSGatien Chevallier #define RNG_DR			U(0x08)
30f3c22059SEtienne Carriere 
31f3c22059SEtienne Carriere #define RNG_CR_RNGEN		BIT(2)
32f3c22059SEtienne Carriere #define RNG_CR_IE		BIT(3)
33f3c22059SEtienne Carriere #define RNG_CR_CED		BIT(5)
34091ef005SGatien Chevallier #define RNG_CR_CLKDIV		GENMASK_32(19, 16)
35091ef005SGatien Chevallier #define RNG_CR_CLKDIV_SHIFT	U(16)
36091ef005SGatien Chevallier #define RNG_CR_CONDRST		BIT(30)
37f3c22059SEtienne Carriere 
38f3c22059SEtienne Carriere #define RNG_SR_DRDY		BIT(0)
39f3c22059SEtienne Carriere #define RNG_SR_CECS		BIT(1)
40f3c22059SEtienne Carriere #define RNG_SR_SECS		BIT(2)
41f3c22059SEtienne Carriere #define RNG_SR_CEIS		BIT(5)
42f3c22059SEtienne Carriere #define RNG_SR_SEIS		BIT(6)
43f3c22059SEtienne Carriere 
440817aa6fSGatien Chevallier #if TRACE_LEVEL > TRACE_DEBUG
450817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US	U(100000)
460817aa6fSGatien Chevallier #else
470817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US	U(10000)
480817aa6fSGatien Chevallier #endif
49ea8ba295SGatien Chevallier #define RNG_RESET_TIMEOUT_US	U(1000)
50f3c22059SEtienne Carriere 
510817aa6fSGatien Chevallier #define RNG_FIFO_BYTE_DEPTH	U(16)
520817aa6fSGatien Chevallier 
53091ef005SGatien Chevallier #define RNG_NIST_CONFIG_A	U(0x0F00D00)
54091ef005SGatien Chevallier #define RNG_NIST_CONFIG_B	U(0x1801000)
55091ef005SGatien Chevallier #define RNG_NIST_CONFIG_MASK	GENMASK_32(25, 8)
56091ef005SGatien Chevallier 
57091ef005SGatien Chevallier #define RNG_MAX_NOISE_CLK_FREQ	U(3000000)
58091ef005SGatien Chevallier 
59091ef005SGatien Chevallier struct stm32_rng_driver_data {
60091ef005SGatien Chevallier 	bool has_cond_reset;
61091ef005SGatien Chevallier };
62091ef005SGatien Chevallier 
63f3c22059SEtienne Carriere struct stm32_rng_instance {
64f3c22059SEtienne Carriere 	struct io_pa_va base;
65d7a1a7d2SEtienne Carriere 	struct clk *clock;
66ea8ba295SGatien Chevallier 	struct rstctrl *rstctrl;
67091ef005SGatien Chevallier 	const struct stm32_rng_driver_data *ddata;
68f3c22059SEtienne Carriere 	unsigned int lock;
69d8682c4cSEtienne Carriere 	bool release_post_boot;
703c752300SGatien Chevallier 	bool clock_error;
71091ef005SGatien Chevallier 	bool error_conceal;
72091ef005SGatien Chevallier 	uint64_t error_to_ref;
73f3c22059SEtienne Carriere };
74f3c22059SEtienne Carriere 
75ea8ba295SGatien Chevallier /* Expect at most a single RNG instance */
76f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng;
77f3c22059SEtienne Carriere 
78f63f11bdSGatien Chevallier static vaddr_t get_base(void)
79f63f11bdSGatien Chevallier {
80f63f11bdSGatien Chevallier 	assert(stm32_rng);
81f63f11bdSGatien Chevallier 
82f63f11bdSGatien Chevallier 	return io_pa_or_va(&stm32_rng->base, 1);
83f63f11bdSGatien Chevallier }
84f63f11bdSGatien Chevallier 
85f3c22059SEtienne Carriere /*
86091ef005SGatien Chevallier  * Extracts from the STM32 RNG specification when RNG supports CONDRST.
87f3c22059SEtienne Carriere  *
88f3c22059SEtienne Carriere  * When a noise source (or seed) error occurs, the RNG stops generating
89f3c22059SEtienne Carriere  * random numbers and sets to “1” both SEIS and SECS bits to indicate
90f3c22059SEtienne Carriere  * that a seed error occurred. (...)
91091ef005SGatien Chevallier  *
92091ef005SGatien Chevallier  * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield
93091ef005SGatien Chevallier  * description for details). This step is needed only if SECS is set.
94091ef005SGatien Chevallier  * Indeed, when SEIS is set and SECS is cleared it means RNG performed
95091ef005SGatien Chevallier  * the reset automatically (auto-reset).
96091ef005SGatien Chevallier  * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST
97091ef005SGatien Chevallier  * to be cleared in the RNG_CR register, then confirm that SEIS is
98091ef005SGatien Chevallier  * cleared in the RNG_SR register. Otherwise just clear SEIS bit in
99091ef005SGatien Chevallier  * the RNG_SR register.
100091ef005SGatien Chevallier  * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be
101091ef005SGatien Chevallier  * cleared by RNG. The random number generation is now back to normal.
102091ef005SGatien Chevallier  */
103091ef005SGatien Chevallier static void conceal_seed_error_cond_reset(void)
104091ef005SGatien Chevallier {
105091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
106091ef005SGatien Chevallier 	vaddr_t rng_base = get_base();
107f3c22059SEtienne Carriere 
108091ef005SGatien Chevallier 	if (!dev->error_conceal) {
109091ef005SGatien Chevallier 		uint32_t sr = io_read32(rng_base + RNG_SR);
110091ef005SGatien Chevallier 
111091ef005SGatien Chevallier 		if (sr & RNG_SR_SECS) {
112091ef005SGatien Chevallier 			/* Conceal by resetting the subsystem (step 1.) */
113091ef005SGatien Chevallier 			io_setbits32(rng_base + RNG_CR, RNG_CR_CONDRST);
114091ef005SGatien Chevallier 			io_clrbits32(rng_base + RNG_CR, RNG_CR_CONDRST);
115091ef005SGatien Chevallier 
116091ef005SGatien Chevallier 			/* Arm timeout for error_conceal sequence */
117091ef005SGatien Chevallier 			dev->error_to_ref =
118091ef005SGatien Chevallier 				timeout_init_us(RNG_READY_TIMEOUT_US);
119091ef005SGatien Chevallier 			dev->error_conceal = true;
120091ef005SGatien Chevallier 		} else {
121091ef005SGatien Chevallier 			/* RNG auto-reset (step 2.) */
122091ef005SGatien Chevallier 			io_clrbits32(rng_base + RNG_SR, RNG_SR_SEIS);
123091ef005SGatien Chevallier 		}
124091ef005SGatien Chevallier 	} else {
125091ef005SGatien Chevallier 		/* Measure time before possible reschedule */
126091ef005SGatien Chevallier 		bool timed_out = timeout_elapsed(dev->error_to_ref);
127091ef005SGatien Chevallier 
128091ef005SGatien Chevallier 		/* Wait CONDRST is cleared (step 2.) */
129091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_CR) & RNG_CR_CONDRST) {
130091ef005SGatien Chevallier 			if (timed_out)
131091ef005SGatien Chevallier 				panic();
132091ef005SGatien Chevallier 
133091ef005SGatien Chevallier 			/* Wait subsystem reset cycle completes */
134091ef005SGatien Chevallier 			return;
135091ef005SGatien Chevallier 		}
136091ef005SGatien Chevallier 
137091ef005SGatien Chevallier 		/* Check SEIS is cleared (step 2.) */
138091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
139091ef005SGatien Chevallier 			panic();
140091ef005SGatien Chevallier 
141091ef005SGatien Chevallier 		/* Wait SECS is cleared (step 3.) */
142091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_SR) & RNG_SR_SECS) {
143091ef005SGatien Chevallier 			if (timed_out)
144091ef005SGatien Chevallier 				panic();
145091ef005SGatien Chevallier 
146091ef005SGatien Chevallier 			/* Wait subsystem reset cycle completes */
147091ef005SGatien Chevallier 			return;
148091ef005SGatien Chevallier 		}
149091ef005SGatien Chevallier 
150091ef005SGatien Chevallier 		dev->error_conceal = false;
151091ef005SGatien Chevallier 	}
152091ef005SGatien Chevallier }
153091ef005SGatien Chevallier 
154091ef005SGatien Chevallier /*
155091ef005SGatien Chevallier  * Extracts from the STM32 RNG specification, when CONDRST is not supported
156091ef005SGatien Chevallier  *
157091ef005SGatien Chevallier  * When a noise source (or seed) error occurs, the RNG stops generating
158091ef005SGatien Chevallier  * random numbers and sets to “1” both SEIS and SECS bits to indicate
159091ef005SGatien Chevallier  * that a seed error occurred. (...)
160091ef005SGatien Chevallier  *
161f3c22059SEtienne Carriere  * The following sequence shall be used to fully recover from a seed
162f3c22059SEtienne Carriere  * error after the RNG initialization:
163f3c22059SEtienne Carriere  * 1. Clear the SEIS bit by writing it to “0”.
164f3c22059SEtienne Carriere  * 2. Read out 12 words from the RNG_DR register, and discard each of
165f3c22059SEtienne Carriere  * them in order to clean the pipeline.
166f3c22059SEtienne Carriere  * 3. Confirm that SEIS is still cleared. Random number generation is
167f3c22059SEtienne Carriere  * back to normal.
168f3c22059SEtienne Carriere  */
169091ef005SGatien Chevallier static void conceal_seed_error_sw_reset(void)
170f3c22059SEtienne Carriere {
1716a6b6168SGatien Chevallier 	vaddr_t rng_base = get_base();
172f3c22059SEtienne Carriere 	size_t i = 0;
173f3c22059SEtienne Carriere 
1746a6b6168SGatien Chevallier 	io_clrbits32(rng_base + RNG_SR, RNG_SR_SEIS);
175f3c22059SEtienne Carriere 
176f3c22059SEtienne Carriere 	for (i = 12; i != 0; i--)
177f3c22059SEtienne Carriere 		(void)io_read32(rng_base + RNG_DR);
178f3c22059SEtienne Carriere 
179f3c22059SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
180f3c22059SEtienne Carriere 		panic("RNG noise");
181f3c22059SEtienne Carriere }
182f3c22059SEtienne Carriere 
183091ef005SGatien Chevallier static void conceal_seed_error(void)
184091ef005SGatien Chevallier {
185091ef005SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset)
186091ef005SGatien Chevallier 		conceal_seed_error_cond_reset();
187091ef005SGatien Chevallier 	else
188091ef005SGatien Chevallier 		conceal_seed_error_sw_reset();
189091ef005SGatien Chevallier }
190091ef005SGatien Chevallier 
191c99311c8SEtienne Carriere static TEE_Result read_available(vaddr_t rng_base, uint8_t *out, size_t *size)
192f3c22059SEtienne Carriere {
193091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
194c99311c8SEtienne Carriere 	uint8_t *buf = NULL;
195c99311c8SEtienne Carriere 	size_t req_size = 0;
196c99311c8SEtienne Carriere 	size_t len = 0;
197f3c22059SEtienne Carriere 
198091ef005SGatien Chevallier 	if (dev->error_conceal || io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
1996a6b6168SGatien Chevallier 		conceal_seed_error();
200f3c22059SEtienne Carriere 
20123123473SEtienne Carriere 	if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) {
20223123473SEtienne Carriere 		FMSG("RNG not ready");
203c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
20423123473SEtienne Carriere 	}
205f3c22059SEtienne Carriere 
20623123473SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) {
20723123473SEtienne Carriere 		FMSG("RNG noise error");
208c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
20923123473SEtienne Carriere 	}
210c99311c8SEtienne Carriere 
211c99311c8SEtienne Carriere 	buf = out;
212c99311c8SEtienne Carriere 	req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size);
213c99311c8SEtienne Carriere 	len = req_size;
214f3c22059SEtienne Carriere 
215f3c22059SEtienne Carriere 	/* RNG is ready: read up to 4 32bit words */
216f3c22059SEtienne Carriere 	while (len) {
217f3c22059SEtienne Carriere 		uint32_t data32 = io_read32(rng_base + RNG_DR);
218f3c22059SEtienne Carriere 		size_t sz = MIN(len, sizeof(uint32_t));
219f3c22059SEtienne Carriere 
220*3e64c635SGatien Chevallier 		/* Late seed error case: DR being 0 is an error status */
221*3e64c635SGatien Chevallier 		if (!data32) {
222*3e64c635SGatien Chevallier 			conceal_seed_error();
223*3e64c635SGatien Chevallier 			return TEE_ERROR_NO_DATA;
224*3e64c635SGatien Chevallier 		}
225*3e64c635SGatien Chevallier 
226f3c22059SEtienne Carriere 		memcpy(buf, &data32, sz);
227f3c22059SEtienne Carriere 		buf += sz;
228f3c22059SEtienne Carriere 		len -= sz;
229f3c22059SEtienne Carriere 	}
230c99311c8SEtienne Carriere 
231f3c22059SEtienne Carriere 	*size = req_size;
232f3c22059SEtienne Carriere 
233c99311c8SEtienne Carriere 	return TEE_SUCCESS;
234f3c22059SEtienne Carriere }
235f3c22059SEtienne Carriere 
236091ef005SGatien Chevallier static uint32_t stm32_rng_clock_freq_restrain(void)
237091ef005SGatien Chevallier {
238091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
239091ef005SGatien Chevallier 	unsigned long clock_rate = 0;
240091ef005SGatien Chevallier 	uint32_t clock_div = 0;
241091ef005SGatien Chevallier 
242091ef005SGatien Chevallier 	clock_rate = clk_get_rate(dev->clock);
243091ef005SGatien Chevallier 
244091ef005SGatien Chevallier 	/*
245091ef005SGatien Chevallier 	 * Get the exponent to apply on the CLKDIV field in RNG_CR register
246091ef005SGatien Chevallier 	 * No need to handle the case when clock-div > 0xF as it is physically
247091ef005SGatien Chevallier 	 * impossible
248091ef005SGatien Chevallier 	 */
249091ef005SGatien Chevallier 	while ((clock_rate >> clock_div) > RNG_MAX_NOISE_CLK_FREQ)
250091ef005SGatien Chevallier 		clock_div++;
251091ef005SGatien Chevallier 
252091ef005SGatien Chevallier 	DMSG("RNG clk rate : %lu", clk_get_rate(dev->clock) >> clock_div);
253091ef005SGatien Chevallier 
254091ef005SGatien Chevallier 	return clock_div;
255091ef005SGatien Chevallier }
256091ef005SGatien Chevallier 
257f63f11bdSGatien Chevallier static TEE_Result init_rng(void)
258f3c22059SEtienne Carriere {
259f63f11bdSGatien Chevallier 	vaddr_t rng_base = get_base();
260f63f11bdSGatien Chevallier 	uint64_t timeout_ref = 0;
2613c752300SGatien Chevallier 	uint32_t cr_ced_mask = 0;
2623c752300SGatien Chevallier 
2633c752300SGatien Chevallier 	if (!stm32_rng->clock_error)
2643c752300SGatien Chevallier 		cr_ced_mask = RNG_CR_CED;
265f3c22059SEtienne Carriere 
266f63f11bdSGatien Chevallier 	/* Clean error indications */
267f63f11bdSGatien Chevallier 	io_write32(rng_base + RNG_SR, 0);
268f3c22059SEtienne Carriere 
269091ef005SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset) {
270091ef005SGatien Chevallier 		uint32_t clock_div = stm32_rng_clock_freq_restrain();
271091ef005SGatien Chevallier 
272091ef005SGatien Chevallier 		/* Update configuration fields */
273091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_NIST_CONFIG_MASK,
274091ef005SGatien Chevallier 				RNG_NIST_CONFIG_B | RNG_CR_CONDRST |
2753c752300SGatien Chevallier 				cr_ced_mask);
276091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_CR_CLKDIV,
277091ef005SGatien Chevallier 				clock_div << RNG_CR_CLKDIV_SHIFT);
278091ef005SGatien Chevallier 
279091ef005SGatien Chevallier 		/* No need to wait for RNG_CR_CONDRST toggle as we enable clk */
280091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_CR_CONDRST,
281091ef005SGatien Chevallier 				RNG_CR_RNGEN);
282091ef005SGatien Chevallier 	} else {
2833c752300SGatien Chevallier 		io_setbits32(rng_base + RNG_CR, RNG_CR_RNGEN | cr_ced_mask);
284091ef005SGatien Chevallier 	}
285f63f11bdSGatien Chevallier 
2860817aa6fSGatien Chevallier 	timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
287f63f11bdSGatien Chevallier 	while (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY))
288f63f11bdSGatien Chevallier 		if (timeout_elapsed(timeout_ref))
289f63f11bdSGatien Chevallier 			break;
290f63f11bdSGatien Chevallier 
291f63f11bdSGatien Chevallier 	if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY))
292f63f11bdSGatien Chevallier 		return TEE_ERROR_GENERIC;
293f63f11bdSGatien Chevallier 
294f63f11bdSGatien Chevallier 	return TEE_SUCCESS;
295f3c22059SEtienne Carriere }
296f3c22059SEtienne Carriere 
297f3c22059SEtienne Carriere TEE_Result stm32_rng_read(uint8_t *out, size_t size)
298f3c22059SEtienne Carriere {
299c99311c8SEtienne Carriere 	TEE_Result rc = TEE_ERROR_GENERIC;
300c99311c8SEtienne Carriere 	bool burst_timeout = false;
301c99311c8SEtienne Carriere 	uint64_t timeout_ref = 0;
302f3c22059SEtienne Carriere 	uint32_t exceptions = 0;
303f3c22059SEtienne Carriere 	uint8_t *out_ptr = out;
304c99311c8SEtienne Carriere 	vaddr_t rng_base = 0;
305f3c22059SEtienne Carriere 	size_t out_size = 0;
306f3c22059SEtienne Carriere 
307f3c22059SEtienne Carriere 	if (!stm32_rng) {
308f3c22059SEtienne Carriere 		DMSG("No RNG");
309f3c22059SEtienne Carriere 		return TEE_ERROR_NOT_SUPPORTED;
310f3c22059SEtienne Carriere 	}
311f3c22059SEtienne Carriere 
312f63f11bdSGatien Chevallier 	clk_enable(stm32_rng->clock);
313f63f11bdSGatien Chevallier 	rng_base = get_base();
314c99311c8SEtienne Carriere 
315c99311c8SEtienne Carriere 	/* Arm timeout */
3160817aa6fSGatien Chevallier 	timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
317c99311c8SEtienne Carriere 	burst_timeout = false;
318f3c22059SEtienne Carriere 
319f3c22059SEtienne Carriere 	while (out_size < size) {
320f3c22059SEtienne Carriere 		/* Read by chunks of the size the RNG FIFO depth */
321f3c22059SEtienne Carriere 		size_t sz = size - out_size;
322f3c22059SEtienne Carriere 
323f3c22059SEtienne Carriere 		exceptions = may_spin_lock(&stm32_rng->lock);
324f3c22059SEtienne Carriere 
325c99311c8SEtienne Carriere 		rc = read_available(rng_base, out_ptr, &sz);
326c99311c8SEtienne Carriere 
327c99311c8SEtienne Carriere 		/* Raise timeout only if we failed to get some samples */
328c99311c8SEtienne Carriere 		assert(!rc || rc == TEE_ERROR_NO_DATA);
329c99311c8SEtienne Carriere 		if (rc)
330c99311c8SEtienne Carriere 			burst_timeout = timeout_elapsed(timeout_ref);
331f3c22059SEtienne Carriere 
332f3c22059SEtienne Carriere 		may_spin_unlock(&stm32_rng->lock, exceptions);
333f3c22059SEtienne Carriere 
334c99311c8SEtienne Carriere 		if (burst_timeout) {
335c99311c8SEtienne Carriere 			rc = TEE_ERROR_GENERIC;
336c99311c8SEtienne Carriere 			goto out;
337f3c22059SEtienne Carriere 		}
338f3c22059SEtienne Carriere 
339c99311c8SEtienne Carriere 		if (!rc) {
340c99311c8SEtienne Carriere 			out_size += sz;
341c99311c8SEtienne Carriere 			out_ptr += sz;
342c99311c8SEtienne Carriere 			/* Re-arm timeout */
3430817aa6fSGatien Chevallier 			timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
344c99311c8SEtienne Carriere 			burst_timeout = false;
345c99311c8SEtienne Carriere 		}
346c99311c8SEtienne Carriere 	}
347c99311c8SEtienne Carriere 
348c99311c8SEtienne Carriere out:
349c99311c8SEtienne Carriere 	assert(!rc || rc == TEE_ERROR_GENERIC);
350f63f11bdSGatien Chevallier 	clk_disable(stm32_rng->clock);
351f3c22059SEtienne Carriere 
352f3c22059SEtienne Carriere 	return rc;
353f3c22059SEtienne Carriere }
354f3c22059SEtienne Carriere 
355cd451498SEtienne Carriere #ifdef CFG_WITH_SOFTWARE_PRNG
356cd451498SEtienne Carriere /* Override weak plat_rng_init with platform handler to seed PRNG */
357cd451498SEtienne Carriere void plat_rng_init(void)
358cd451498SEtienne Carriere {
359cd451498SEtienne Carriere 	uint8_t seed[RNG_FIFO_BYTE_DEPTH] = { };
360cd451498SEtienne Carriere 
361cd451498SEtienne Carriere 	if (stm32_rng_read(seed, sizeof(seed)))
362cd451498SEtienne Carriere 		panic();
363cd451498SEtienne Carriere 
364cd451498SEtienne Carriere 	if (crypto_rng_init(seed, sizeof(seed)))
365cd451498SEtienne Carriere 		panic();
366cd451498SEtienne Carriere 
367cd451498SEtienne Carriere 	DMSG("PRNG seeded with RNG");
368cd451498SEtienne Carriere }
369cd451498SEtienne Carriere #else
370cb2478efSAndrew Davis TEE_Result hw_get_random_bytes(void *out, size_t size)
371097f329aSEtienne Carriere {
372097f329aSEtienne Carriere 	return stm32_rng_read(out, size);
373097f329aSEtienne Carriere }
374097f329aSEtienne Carriere #endif
375097f329aSEtienne Carriere 
37629893549SGatien Chevallier static TEE_Result stm32_rng_pm_resume(uint32_t pm_cr)
37729893549SGatien Chevallier {
37829893549SGatien Chevallier 	vaddr_t base = get_base();
37929893549SGatien Chevallier 
38029893549SGatien Chevallier 	/* Clean error indications */
38129893549SGatien Chevallier 	io_write32(base + RNG_SR, 0);
38229893549SGatien Chevallier 
38329893549SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset) {
38429893549SGatien Chevallier 		/*
38529893549SGatien Chevallier 		 * Correct configuration in bits [29:4] must be set in the same
38629893549SGatien Chevallier 		 * access that set RNG_CR_CONDRST bit. Else config setting is
38729893549SGatien Chevallier 		 * not taken into account.
38829893549SGatien Chevallier 		 */
38929893549SGatien Chevallier 		io_write32(base + RNG_CR, pm_cr | RNG_CR_CONDRST);
39029893549SGatien Chevallier 
39129893549SGatien Chevallier 		io_clrsetbits32(base + RNG_CR, RNG_CR_CONDRST, RNG_CR_RNGEN);
39229893549SGatien Chevallier 	} else {
39329893549SGatien Chevallier 		io_write32(base + RNG_CR, RNG_CR_RNGEN | pm_cr);
39429893549SGatien Chevallier 	}
39529893549SGatien Chevallier 
39629893549SGatien Chevallier 	return TEE_SUCCESS;
39729893549SGatien Chevallier }
39829893549SGatien Chevallier 
39929893549SGatien Chevallier static TEE_Result
40029893549SGatien Chevallier stm32_rng_pm(enum pm_op op, unsigned int pm_hint __unused,
40129893549SGatien Chevallier 	     const struct pm_callback_handle *pm_handle __unused)
40229893549SGatien Chevallier {
40329893549SGatien Chevallier 	static uint32_t pm_cr;
40429893549SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
40529893549SGatien Chevallier 
40629893549SGatien Chevallier 	assert(stm32_rng && (op == PM_OP_SUSPEND || op == PM_OP_RESUME));
40729893549SGatien Chevallier 
40829893549SGatien Chevallier 	res = clk_enable(stm32_rng->clock);
40929893549SGatien Chevallier 	if (res)
41029893549SGatien Chevallier 		return res;
41129893549SGatien Chevallier 
41229893549SGatien Chevallier 	if (op == PM_OP_SUSPEND)
41329893549SGatien Chevallier 		pm_cr = io_read32(get_base() + RNG_CR);
41429893549SGatien Chevallier 	else
41529893549SGatien Chevallier 		res = stm32_rng_pm_resume(pm_cr);
41629893549SGatien Chevallier 
41729893549SGatien Chevallier 	clk_disable(stm32_rng->clock);
41829893549SGatien Chevallier 
41929893549SGatien Chevallier 	return res;
42029893549SGatien Chevallier }
42129893549SGatien Chevallier DECLARE_KEEP_PAGER(stm32_rng_pm);
42229893549SGatien Chevallier 
423f3c22059SEtienne Carriere #ifdef CFG_EMBED_DTB
424ea8ba295SGatien Chevallier static TEE_Result stm32_rng_parse_fdt(const void *fdt, int node)
425f3c22059SEtienne Carriere {
426d7a1a7d2SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
427ea8ba295SGatien Chevallier 	struct dt_node_info dt_rng = { };
428f3c22059SEtienne Carriere 
429ea8ba295SGatien Chevallier 	_fdt_fill_device_info(fdt, &dt_rng, node);
430ea8ba295SGatien Chevallier 	if (dt_rng.reg == DT_INFO_INVALID_REG)
431ea8ba295SGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
432f3c22059SEtienne Carriere 
433ea8ba295SGatien Chevallier 	stm32_rng->base.pa = dt_rng.reg;
434ea8ba295SGatien Chevallier 	stm32_rng->base.va = io_pa_or_va_secure(&stm32_rng->base,
435ea8ba295SGatien Chevallier 						dt_rng.reg_size);
436ea8ba295SGatien Chevallier 	assert(stm32_rng->base.va);
437f3c22059SEtienne Carriere 
438ea8ba295SGatien Chevallier 	res = rstctrl_dt_get_by_index(fdt, node, 0, &stm32_rng->rstctrl);
439ea8ba295SGatien Chevallier 	if (res != TEE_SUCCESS && res != TEE_ERROR_ITEM_NOT_FOUND)
440ea8ba295SGatien Chevallier 		return res;
44168c4a16bSEtienne Carriere 
442d7a1a7d2SEtienne Carriere 	res = clk_dt_get_by_index(fdt, node, 0, &stm32_rng->clock);
443d7a1a7d2SEtienne Carriere 	if (res)
444d7a1a7d2SEtienne Carriere 		return res;
445d7a1a7d2SEtienne Carriere 
4463c752300SGatien Chevallier 	if (fdt_getprop(fdt, node, "clock-error-detect", NULL))
4473c752300SGatien Chevallier 		stm32_rng->clock_error = true;
4483c752300SGatien Chevallier 
449ea8ba295SGatien Chevallier 	/* Release device if not used at runtime or for pm transitions */
450ea8ba295SGatien Chevallier 	stm32_rng->release_post_boot = IS_ENABLED(CFG_WITH_SOFTWARE_PRNG) &&
451ea8ba295SGatien Chevallier 				       !IS_ENABLED(CFG_PM);
452f3c22059SEtienne Carriere 
453f3c22059SEtienne Carriere 	return TEE_SUCCESS;
454f3c22059SEtienne Carriere }
455f3c22059SEtienne Carriere 
456ea8ba295SGatien Chevallier static TEE_Result stm32_rng_probe(const void *fdt, int offs,
457ea8ba295SGatien Chevallier 				  const void *compat_data __unused)
458ea8ba295SGatien Chevallier {
459ea8ba295SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
460ea8ba295SGatien Chevallier 
461ea8ba295SGatien Chevallier 	/* Expect a single RNG instance */
462ea8ba295SGatien Chevallier 	assert(!stm32_rng);
463ea8ba295SGatien Chevallier 
464ea8ba295SGatien Chevallier 	stm32_rng = calloc(1, sizeof(*stm32_rng));
465ea8ba295SGatien Chevallier 	if (!stm32_rng)
466ea8ba295SGatien Chevallier 		panic();
467ea8ba295SGatien Chevallier 
468ea8ba295SGatien Chevallier 	res = stm32_rng_parse_fdt(fdt, offs);
469ea8ba295SGatien Chevallier 	if (res)
470ea8ba295SGatien Chevallier 		goto err;
471ea8ba295SGatien Chevallier 
472091ef005SGatien Chevallier 	stm32_rng->ddata = compat_data;
473091ef005SGatien Chevallier 	assert(stm32_rng->ddata);
474091ef005SGatien Chevallier 
475ea8ba295SGatien Chevallier 	res = clk_enable(stm32_rng->clock);
476ea8ba295SGatien Chevallier 	if (res)
477ea8ba295SGatien Chevallier 		goto err;
478ea8ba295SGatien Chevallier 
479ea8ba295SGatien Chevallier 	if (stm32_rng->rstctrl &&
480ea8ba295SGatien Chevallier 	    rstctrl_assert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) {
481ea8ba295SGatien Chevallier 		res = TEE_ERROR_GENERIC;
482ea8ba295SGatien Chevallier 		goto err_clk;
483ea8ba295SGatien Chevallier 	}
484ea8ba295SGatien Chevallier 
485ea8ba295SGatien Chevallier 	if (stm32_rng->rstctrl &&
486ea8ba295SGatien Chevallier 	    rstctrl_deassert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) {
487ea8ba295SGatien Chevallier 		res = TEE_ERROR_GENERIC;
488ea8ba295SGatien Chevallier 		goto err_clk;
489ea8ba295SGatien Chevallier 	}
490ea8ba295SGatien Chevallier 
491f63f11bdSGatien Chevallier 	res = init_rng();
492f63f11bdSGatien Chevallier 	if (res)
493f63f11bdSGatien Chevallier 		goto err_clk;
494f63f11bdSGatien Chevallier 
495ea8ba295SGatien Chevallier 	clk_disable(stm32_rng->clock);
496ea8ba295SGatien Chevallier 
497ea8ba295SGatien Chevallier 	if (stm32_rng->release_post_boot)
498ea8ba295SGatien Chevallier 		stm32mp_register_non_secure_periph_iomem(stm32_rng->base.pa);
499ea8ba295SGatien Chevallier 	else
500ea8ba295SGatien Chevallier 		stm32mp_register_secure_periph_iomem(stm32_rng->base.pa);
501ea8ba295SGatien Chevallier 
50229893549SGatien Chevallier 	register_pm_core_service_cb(stm32_rng_pm, &stm32_rng, "rng-service");
50329893549SGatien Chevallier 
504ea8ba295SGatien Chevallier 	return TEE_SUCCESS;
505ea8ba295SGatien Chevallier 
506ea8ba295SGatien Chevallier err_clk:
507ea8ba295SGatien Chevallier 	clk_disable(stm32_rng->clock);
508ea8ba295SGatien Chevallier err:
509ea8ba295SGatien Chevallier 	free(stm32_rng);
510ea8ba295SGatien Chevallier 	stm32_rng = NULL;
511ea8ba295SGatien Chevallier 
512ea8ba295SGatien Chevallier 	return res;
513ea8ba295SGatien Chevallier }
514ea8ba295SGatien Chevallier 
515091ef005SGatien Chevallier static const struct stm32_rng_driver_data mp13_data[] = {
516091ef005SGatien Chevallier 	{ .has_cond_reset = true },
517091ef005SGatien Chevallier };
518091ef005SGatien Chevallier 
519091ef005SGatien Chevallier static const struct stm32_rng_driver_data mp15_data[] = {
520091ef005SGatien Chevallier 	{ .has_cond_reset = false },
521091ef005SGatien Chevallier };
522091ef005SGatien Chevallier DECLARE_KEEP_PAGER(mp15_data);
523091ef005SGatien Chevallier 
524ea8ba295SGatien Chevallier static const struct dt_device_match rng_match_table[] = {
525091ef005SGatien Chevallier 	{ .compatible = "st,stm32-rng", .compat_data = &mp15_data },
526091ef005SGatien Chevallier 	{ .compatible = "st,stm32mp13-rng", .compat_data = &mp13_data },
527ea8ba295SGatien Chevallier 	{ }
528ea8ba295SGatien Chevallier };
529ea8ba295SGatien Chevallier 
530ea8ba295SGatien Chevallier DEFINE_DT_DRIVER(stm32_rng_dt_driver) = {
531ea8ba295SGatien Chevallier 	.name = "stm32_rng",
532ea8ba295SGatien Chevallier 	.match_table = rng_match_table,
533ea8ba295SGatien Chevallier 	.probe = stm32_rng_probe,
534ea8ba295SGatien Chevallier };
535d8682c4cSEtienne Carriere 
536d8682c4cSEtienne Carriere static TEE_Result stm32_rng_release(void)
537d8682c4cSEtienne Carriere {
538d8682c4cSEtienne Carriere 	if (stm32_rng && stm32_rng->release_post_boot) {
539d8682c4cSEtienne Carriere 		DMSG("Release RNG driver");
540d8682c4cSEtienne Carriere 		free(stm32_rng);
541d8682c4cSEtienne Carriere 		stm32_rng = NULL;
542d8682c4cSEtienne Carriere 	}
543d8682c4cSEtienne Carriere 
544d8682c4cSEtienne Carriere 	return TEE_SUCCESS;
545d8682c4cSEtienne Carriere }
546d8682c4cSEtienne Carriere 
547d8682c4cSEtienne Carriere release_init_resource(stm32_rng_release);
548f3c22059SEtienne Carriere #endif /*CFG_EMBED_DTB*/
549