xref: /optee_os/core/drivers/stm32_rng.c (revision 45da6509d925960a65423ec87f5be85b6a14b0b8)
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 <io.h>
11f3c22059SEtienne Carriere #include <kernel/delay.h>
12a2fc83d1SJerome Forissier #include <kernel/dt.h>
13ea8ba295SGatien Chevallier #include <kernel/dt_driver.h>
1465401337SJens Wiklander #include <kernel/boot.h>
15f3c22059SEtienne Carriere #include <kernel/panic.h>
1629893549SGatien Chevallier #include <kernel/pm.h>
1765b5ada4SMarouene Boubakri #include <kernel/thread.h>
18a2fc83d1SJerome Forissier #include <libfdt.h>
19f3c22059SEtienne Carriere #include <mm/core_memprot.h>
20097f329aSEtienne Carriere #include <rng_support.h>
21f3c22059SEtienne Carriere #include <stdbool.h>
22f3c22059SEtienne Carriere #include <stm32_util.h>
23f3c22059SEtienne Carriere #include <string.h>
24cd451498SEtienne Carriere #include <tee/tee_cryp_utl.h>
25f3c22059SEtienne Carriere 
260817aa6fSGatien Chevallier #define RNG_CR			U(0x00)
270817aa6fSGatien Chevallier #define RNG_SR			U(0x04)
280817aa6fSGatien Chevallier #define RNG_DR			U(0x08)
29f3c22059SEtienne Carriere 
30f3c22059SEtienne Carriere #define RNG_CR_RNGEN		BIT(2)
31f3c22059SEtienne Carriere #define RNG_CR_IE		BIT(3)
32f3c22059SEtienne Carriere #define RNG_CR_CED		BIT(5)
33091ef005SGatien Chevallier #define RNG_CR_CLKDIV		GENMASK_32(19, 16)
34091ef005SGatien Chevallier #define RNG_CR_CLKDIV_SHIFT	U(16)
35091ef005SGatien Chevallier #define RNG_CR_CONDRST		BIT(30)
36f3c22059SEtienne Carriere 
37f3c22059SEtienne Carriere #define RNG_SR_DRDY		BIT(0)
38f3c22059SEtienne Carriere #define RNG_SR_CECS		BIT(1)
39f3c22059SEtienne Carriere #define RNG_SR_SECS		BIT(2)
40f3c22059SEtienne Carriere #define RNG_SR_CEIS		BIT(5)
41f3c22059SEtienne Carriere #define RNG_SR_SEIS		BIT(6)
42f3c22059SEtienne Carriere 
430817aa6fSGatien Chevallier #if TRACE_LEVEL > TRACE_DEBUG
440817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US	U(100000)
450817aa6fSGatien Chevallier #else
460817aa6fSGatien Chevallier #define RNG_READY_TIMEOUT_US	U(10000)
470817aa6fSGatien Chevallier #endif
48ea8ba295SGatien Chevallier #define RNG_RESET_TIMEOUT_US	U(1000)
49f3c22059SEtienne Carriere 
500817aa6fSGatien Chevallier #define RNG_FIFO_BYTE_DEPTH	U(16)
510817aa6fSGatien Chevallier 
52091ef005SGatien Chevallier #define RNG_NIST_CONFIG_A	U(0x0F00D00)
53091ef005SGatien Chevallier #define RNG_NIST_CONFIG_B	U(0x1801000)
54091ef005SGatien Chevallier #define RNG_NIST_CONFIG_MASK	GENMASK_32(25, 8)
55091ef005SGatien Chevallier 
56091ef005SGatien Chevallier #define RNG_MAX_NOISE_CLK_FREQ	U(3000000)
57091ef005SGatien Chevallier 
58091ef005SGatien Chevallier struct stm32_rng_driver_data {
59*45da6509SGatien Chevallier 	unsigned long nb_clock;
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;
66*45da6509SGatien Chevallier 	struct clk *bus_clock;
67ea8ba295SGatien Chevallier 	struct rstctrl *rstctrl;
68091ef005SGatien Chevallier 	const struct stm32_rng_driver_data *ddata;
69f3c22059SEtienne Carriere 	unsigned int lock;
70d8682c4cSEtienne Carriere 	bool release_post_boot;
713c752300SGatien Chevallier 	bool clock_error;
72091ef005SGatien Chevallier 	bool error_conceal;
73091ef005SGatien Chevallier 	uint64_t error_to_ref;
74f3c22059SEtienne Carriere };
75f3c22059SEtienne Carriere 
76ea8ba295SGatien Chevallier /* Expect at most a single RNG instance */
77f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng;
78f3c22059SEtienne Carriere 
79f63f11bdSGatien Chevallier static vaddr_t get_base(void)
80f63f11bdSGatien Chevallier {
81f63f11bdSGatien Chevallier 	assert(stm32_rng);
82f63f11bdSGatien Chevallier 
83f63f11bdSGatien Chevallier 	return io_pa_or_va(&stm32_rng->base, 1);
84f63f11bdSGatien Chevallier }
85f63f11bdSGatien Chevallier 
86f3c22059SEtienne Carriere /*
87091ef005SGatien Chevallier  * Extracts from the STM32 RNG specification when RNG supports CONDRST.
88f3c22059SEtienne Carriere  *
89f3c22059SEtienne Carriere  * When a noise source (or seed) error occurs, the RNG stops generating
90f3c22059SEtienne Carriere  * random numbers and sets to “1” both SEIS and SECS bits to indicate
91f3c22059SEtienne Carriere  * that a seed error occurred. (...)
92091ef005SGatien Chevallier  *
93091ef005SGatien Chevallier  * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield
94091ef005SGatien Chevallier  * description for details). This step is needed only if SECS is set.
95091ef005SGatien Chevallier  * Indeed, when SEIS is set and SECS is cleared it means RNG performed
96091ef005SGatien Chevallier  * the reset automatically (auto-reset).
97091ef005SGatien Chevallier  * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST
98091ef005SGatien Chevallier  * to be cleared in the RNG_CR register, then confirm that SEIS is
99091ef005SGatien Chevallier  * cleared in the RNG_SR register. Otherwise just clear SEIS bit in
100091ef005SGatien Chevallier  * the RNG_SR register.
101091ef005SGatien Chevallier  * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be
102091ef005SGatien Chevallier  * cleared by RNG. The random number generation is now back to normal.
103091ef005SGatien Chevallier  */
104091ef005SGatien Chevallier static void conceal_seed_error_cond_reset(void)
105091ef005SGatien Chevallier {
106091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
107091ef005SGatien Chevallier 	vaddr_t rng_base = get_base();
108f3c22059SEtienne Carriere 
109091ef005SGatien Chevallier 	if (!dev->error_conceal) {
110091ef005SGatien Chevallier 		uint32_t sr = io_read32(rng_base + RNG_SR);
111091ef005SGatien Chevallier 
112091ef005SGatien Chevallier 		if (sr & RNG_SR_SECS) {
113091ef005SGatien Chevallier 			/* Conceal by resetting the subsystem (step 1.) */
114091ef005SGatien Chevallier 			io_setbits32(rng_base + RNG_CR, RNG_CR_CONDRST);
115091ef005SGatien Chevallier 			io_clrbits32(rng_base + RNG_CR, RNG_CR_CONDRST);
116091ef005SGatien Chevallier 
117091ef005SGatien Chevallier 			/* Arm timeout for error_conceal sequence */
118091ef005SGatien Chevallier 			dev->error_to_ref =
119091ef005SGatien Chevallier 				timeout_init_us(RNG_READY_TIMEOUT_US);
120091ef005SGatien Chevallier 			dev->error_conceal = true;
121091ef005SGatien Chevallier 		} else {
122091ef005SGatien Chevallier 			/* RNG auto-reset (step 2.) */
123091ef005SGatien Chevallier 			io_clrbits32(rng_base + RNG_SR, RNG_SR_SEIS);
124091ef005SGatien Chevallier 		}
125091ef005SGatien Chevallier 	} else {
126091ef005SGatien Chevallier 		/* Measure time before possible reschedule */
127091ef005SGatien Chevallier 		bool timed_out = timeout_elapsed(dev->error_to_ref);
128091ef005SGatien Chevallier 
129091ef005SGatien Chevallier 		/* Wait CONDRST is cleared (step 2.) */
130091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_CR) & RNG_CR_CONDRST) {
131091ef005SGatien Chevallier 			if (timed_out)
132091ef005SGatien Chevallier 				panic();
133091ef005SGatien Chevallier 
134091ef005SGatien Chevallier 			/* Wait subsystem reset cycle completes */
135091ef005SGatien Chevallier 			return;
136091ef005SGatien Chevallier 		}
137091ef005SGatien Chevallier 
138091ef005SGatien Chevallier 		/* Check SEIS is cleared (step 2.) */
139091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
140091ef005SGatien Chevallier 			panic();
141091ef005SGatien Chevallier 
142091ef005SGatien Chevallier 		/* Wait SECS is cleared (step 3.) */
143091ef005SGatien Chevallier 		if (io_read32(rng_base + RNG_SR) & RNG_SR_SECS) {
144091ef005SGatien Chevallier 			if (timed_out)
145091ef005SGatien Chevallier 				panic();
146091ef005SGatien Chevallier 
147091ef005SGatien Chevallier 			/* Wait subsystem reset cycle completes */
148091ef005SGatien Chevallier 			return;
149091ef005SGatien Chevallier 		}
150091ef005SGatien Chevallier 
151091ef005SGatien Chevallier 		dev->error_conceal = false;
152091ef005SGatien Chevallier 	}
153091ef005SGatien Chevallier }
154091ef005SGatien Chevallier 
155091ef005SGatien Chevallier /*
156091ef005SGatien Chevallier  * Extracts from the STM32 RNG specification, when CONDRST is not supported
157091ef005SGatien Chevallier  *
158091ef005SGatien Chevallier  * When a noise source (or seed) error occurs, the RNG stops generating
159091ef005SGatien Chevallier  * random numbers and sets to “1” both SEIS and SECS bits to indicate
160091ef005SGatien Chevallier  * that a seed error occurred. (...)
161091ef005SGatien Chevallier  *
162f3c22059SEtienne Carriere  * The following sequence shall be used to fully recover from a seed
163f3c22059SEtienne Carriere  * error after the RNG initialization:
164f3c22059SEtienne Carriere  * 1. Clear the SEIS bit by writing it to “0”.
165f3c22059SEtienne Carriere  * 2. Read out 12 words from the RNG_DR register, and discard each of
166f3c22059SEtienne Carriere  * them in order to clean the pipeline.
167f3c22059SEtienne Carriere  * 3. Confirm that SEIS is still cleared. Random number generation is
168f3c22059SEtienne Carriere  * back to normal.
169f3c22059SEtienne Carriere  */
170091ef005SGatien Chevallier static void conceal_seed_error_sw_reset(void)
171f3c22059SEtienne Carriere {
1726a6b6168SGatien Chevallier 	vaddr_t rng_base = get_base();
173f3c22059SEtienne Carriere 	size_t i = 0;
174f3c22059SEtienne Carriere 
1756a6b6168SGatien Chevallier 	io_clrbits32(rng_base + RNG_SR, RNG_SR_SEIS);
176f3c22059SEtienne Carriere 
177f3c22059SEtienne Carriere 	for (i = 12; i != 0; i--)
178f3c22059SEtienne Carriere 		(void)io_read32(rng_base + RNG_DR);
179f3c22059SEtienne Carriere 
180f3c22059SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
181f3c22059SEtienne Carriere 		panic("RNG noise");
182f3c22059SEtienne Carriere }
183f3c22059SEtienne Carriere 
184091ef005SGatien Chevallier static void conceal_seed_error(void)
185091ef005SGatien Chevallier {
186091ef005SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset)
187091ef005SGatien Chevallier 		conceal_seed_error_cond_reset();
188091ef005SGatien Chevallier 	else
189091ef005SGatien Chevallier 		conceal_seed_error_sw_reset();
190091ef005SGatien Chevallier }
191091ef005SGatien Chevallier 
192c99311c8SEtienne Carriere static TEE_Result read_available(vaddr_t rng_base, uint8_t *out, size_t *size)
193f3c22059SEtienne Carriere {
194091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
195c99311c8SEtienne Carriere 	uint8_t *buf = NULL;
196c99311c8SEtienne Carriere 	size_t req_size = 0;
197c99311c8SEtienne Carriere 	size_t len = 0;
198f3c22059SEtienne Carriere 
199091ef005SGatien Chevallier 	if (dev->error_conceal || io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
2006a6b6168SGatien Chevallier 		conceal_seed_error();
201f3c22059SEtienne Carriere 
20223123473SEtienne Carriere 	if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) {
20323123473SEtienne Carriere 		FMSG("RNG not ready");
204c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
20523123473SEtienne Carriere 	}
206f3c22059SEtienne Carriere 
20723123473SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) {
20823123473SEtienne Carriere 		FMSG("RNG noise error");
209c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
21023123473SEtienne Carriere 	}
211c99311c8SEtienne Carriere 
212c99311c8SEtienne Carriere 	buf = out;
213c99311c8SEtienne Carriere 	req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size);
214c99311c8SEtienne Carriere 	len = req_size;
215f3c22059SEtienne Carriere 
216f3c22059SEtienne Carriere 	/* RNG is ready: read up to 4 32bit words */
217f3c22059SEtienne Carriere 	while (len) {
21823bdf063SEtienne Carriere 		uint32_t data32 = 0;
219f3c22059SEtienne Carriere 		size_t sz = MIN(len, sizeof(uint32_t));
220f3c22059SEtienne Carriere 
22123bdf063SEtienne Carriere 		if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY))
22223bdf063SEtienne Carriere 			break;
22323bdf063SEtienne Carriere 		data32 = io_read32(rng_base + RNG_DR);
22423bdf063SEtienne Carriere 
2253e64c635SGatien Chevallier 		/* Late seed error case: DR being 0 is an error status */
2263e64c635SGatien Chevallier 		if (!data32) {
2273e64c635SGatien Chevallier 			conceal_seed_error();
2283e64c635SGatien Chevallier 			return TEE_ERROR_NO_DATA;
2293e64c635SGatien Chevallier 		}
2303e64c635SGatien Chevallier 
231f3c22059SEtienne Carriere 		memcpy(buf, &data32, sz);
232f3c22059SEtienne Carriere 		buf += sz;
233f3c22059SEtienne Carriere 		len -= sz;
234f3c22059SEtienne Carriere 	}
235c99311c8SEtienne Carriere 
23623bdf063SEtienne Carriere 	*size = req_size - len;
237f3c22059SEtienne Carriere 
238c99311c8SEtienne Carriere 	return TEE_SUCCESS;
239f3c22059SEtienne Carriere }
240f3c22059SEtienne Carriere 
241091ef005SGatien Chevallier static uint32_t stm32_rng_clock_freq_restrain(void)
242091ef005SGatien Chevallier {
243091ef005SGatien Chevallier 	struct stm32_rng_instance *dev = stm32_rng;
244091ef005SGatien Chevallier 	unsigned long clock_rate = 0;
245091ef005SGatien Chevallier 	uint32_t clock_div = 0;
246091ef005SGatien Chevallier 
247091ef005SGatien Chevallier 	clock_rate = clk_get_rate(dev->clock);
248091ef005SGatien Chevallier 
249091ef005SGatien Chevallier 	/*
250091ef005SGatien Chevallier 	 * Get the exponent to apply on the CLKDIV field in RNG_CR register
251091ef005SGatien Chevallier 	 * No need to handle the case when clock-div > 0xF as it is physically
252091ef005SGatien Chevallier 	 * impossible
253091ef005SGatien Chevallier 	 */
254091ef005SGatien Chevallier 	while ((clock_rate >> clock_div) > RNG_MAX_NOISE_CLK_FREQ)
255091ef005SGatien Chevallier 		clock_div++;
256091ef005SGatien Chevallier 
257091ef005SGatien Chevallier 	DMSG("RNG clk rate : %lu", clk_get_rate(dev->clock) >> clock_div);
258091ef005SGatien Chevallier 
259091ef005SGatien Chevallier 	return clock_div;
260091ef005SGatien Chevallier }
261091ef005SGatien Chevallier 
262f63f11bdSGatien Chevallier static TEE_Result init_rng(void)
263f3c22059SEtienne Carriere {
264f63f11bdSGatien Chevallier 	vaddr_t rng_base = get_base();
265f63f11bdSGatien Chevallier 	uint64_t timeout_ref = 0;
2663c752300SGatien Chevallier 	uint32_t cr_ced_mask = 0;
2673c752300SGatien Chevallier 
2683c752300SGatien Chevallier 	if (!stm32_rng->clock_error)
2693c752300SGatien Chevallier 		cr_ced_mask = RNG_CR_CED;
270f3c22059SEtienne Carriere 
271f63f11bdSGatien Chevallier 	/* Clean error indications */
272f63f11bdSGatien Chevallier 	io_write32(rng_base + RNG_SR, 0);
273f3c22059SEtienne Carriere 
274091ef005SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset) {
275091ef005SGatien Chevallier 		uint32_t clock_div = stm32_rng_clock_freq_restrain();
276091ef005SGatien Chevallier 
277091ef005SGatien Chevallier 		/* Update configuration fields */
278091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_NIST_CONFIG_MASK,
279091ef005SGatien Chevallier 				RNG_NIST_CONFIG_B | RNG_CR_CONDRST |
2803c752300SGatien Chevallier 				cr_ced_mask);
281091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_CR_CLKDIV,
282091ef005SGatien Chevallier 				clock_div << RNG_CR_CLKDIV_SHIFT);
283091ef005SGatien Chevallier 
284091ef005SGatien Chevallier 		io_clrsetbits32(rng_base + RNG_CR, RNG_CR_CONDRST,
285091ef005SGatien Chevallier 				RNG_CR_RNGEN);
286eb5cf770SGatien Chevallier 
287eb5cf770SGatien Chevallier 		timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
288eb5cf770SGatien Chevallier 		while (io_read32(rng_base + RNG_CR) & RNG_CR_CONDRST)
289eb5cf770SGatien Chevallier 			if (timeout_elapsed(timeout_ref))
290eb5cf770SGatien Chevallier 				break;
291eb5cf770SGatien Chevallier 		if (io_read32(rng_base + RNG_CR) & RNG_CR_CONDRST)
292eb5cf770SGatien Chevallier 			panic();
293091ef005SGatien Chevallier 	} else {
2943c752300SGatien Chevallier 		io_setbits32(rng_base + RNG_CR, RNG_CR_RNGEN | cr_ced_mask);
295091ef005SGatien Chevallier 	}
296f63f11bdSGatien Chevallier 
2970817aa6fSGatien Chevallier 	timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
298f63f11bdSGatien Chevallier 	while (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY))
299f63f11bdSGatien Chevallier 		if (timeout_elapsed(timeout_ref))
300f63f11bdSGatien Chevallier 			break;
301f63f11bdSGatien Chevallier 
302f63f11bdSGatien Chevallier 	if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY))
303f63f11bdSGatien Chevallier 		return TEE_ERROR_GENERIC;
304f63f11bdSGatien Chevallier 
305f63f11bdSGatien Chevallier 	return TEE_SUCCESS;
306f3c22059SEtienne Carriere }
307f3c22059SEtienne Carriere 
30898c36268SGatien Chevallier static TEE_Result stm32_rng_read(uint8_t *out, size_t size)
309f3c22059SEtienne Carriere {
310c99311c8SEtienne Carriere 	TEE_Result rc = TEE_ERROR_GENERIC;
311c99311c8SEtienne Carriere 	bool burst_timeout = false;
312c99311c8SEtienne Carriere 	uint64_t timeout_ref = 0;
313f3c22059SEtienne Carriere 	uint32_t exceptions = 0;
314f3c22059SEtienne Carriere 	uint8_t *out_ptr = out;
315c99311c8SEtienne Carriere 	vaddr_t rng_base = 0;
316f3c22059SEtienne Carriere 	size_t out_size = 0;
317f3c22059SEtienne Carriere 
318f3c22059SEtienne Carriere 	if (!stm32_rng) {
319f3c22059SEtienne Carriere 		DMSG("No RNG");
320f3c22059SEtienne Carriere 		return TEE_ERROR_NOT_SUPPORTED;
321f3c22059SEtienne Carriere 	}
322f3c22059SEtienne Carriere 
323fb1681dfSGatien Chevallier 	rc = clk_enable(stm32_rng->clock);
324fb1681dfSGatien Chevallier 	if (rc)
325fb1681dfSGatien Chevallier 		return rc;
326fb1681dfSGatien Chevallier 
327*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock) {
328*45da6509SGatien Chevallier 		rc = clk_enable(stm32_rng->bus_clock);
329*45da6509SGatien Chevallier 		if (rc) {
330*45da6509SGatien Chevallier 			clk_disable(stm32_rng->clock);
331*45da6509SGatien Chevallier 			return rc;
332*45da6509SGatien Chevallier 		}
333*45da6509SGatien Chevallier 	}
334*45da6509SGatien Chevallier 
335f63f11bdSGatien Chevallier 	rng_base = get_base();
336c99311c8SEtienne Carriere 
337c99311c8SEtienne Carriere 	/* Arm timeout */
3380817aa6fSGatien Chevallier 	timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
339c99311c8SEtienne Carriere 	burst_timeout = false;
340f3c22059SEtienne Carriere 
341f3c22059SEtienne Carriere 	while (out_size < size) {
342f3c22059SEtienne Carriere 		/* Read by chunks of the size the RNG FIFO depth */
343f3c22059SEtienne Carriere 		size_t sz = size - out_size;
344f3c22059SEtienne Carriere 
345f3c22059SEtienne Carriere 		exceptions = may_spin_lock(&stm32_rng->lock);
346f3c22059SEtienne Carriere 
347c99311c8SEtienne Carriere 		rc = read_available(rng_base, out_ptr, &sz);
348c99311c8SEtienne Carriere 
349c99311c8SEtienne Carriere 		/* Raise timeout only if we failed to get some samples */
350c99311c8SEtienne Carriere 		assert(!rc || rc == TEE_ERROR_NO_DATA);
351c99311c8SEtienne Carriere 		if (rc)
352c99311c8SEtienne Carriere 			burst_timeout = timeout_elapsed(timeout_ref);
353f3c22059SEtienne Carriere 
354f3c22059SEtienne Carriere 		may_spin_unlock(&stm32_rng->lock, exceptions);
355f3c22059SEtienne Carriere 
356c99311c8SEtienne Carriere 		if (burst_timeout) {
357c99311c8SEtienne Carriere 			rc = TEE_ERROR_GENERIC;
358c99311c8SEtienne Carriere 			goto out;
359f3c22059SEtienne Carriere 		}
360f3c22059SEtienne Carriere 
361c99311c8SEtienne Carriere 		if (!rc) {
362c99311c8SEtienne Carriere 			out_size += sz;
363c99311c8SEtienne Carriere 			out_ptr += sz;
364c99311c8SEtienne Carriere 			/* Re-arm timeout */
3650817aa6fSGatien Chevallier 			timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
366c99311c8SEtienne Carriere 			burst_timeout = false;
367c99311c8SEtienne Carriere 		}
368c99311c8SEtienne Carriere 	}
369c99311c8SEtienne Carriere 
370c99311c8SEtienne Carriere out:
371c99311c8SEtienne Carriere 	assert(!rc || rc == TEE_ERROR_GENERIC);
372f63f11bdSGatien Chevallier 	clk_disable(stm32_rng->clock);
373*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock)
374*45da6509SGatien Chevallier 		clk_disable(stm32_rng->bus_clock);
375f3c22059SEtienne Carriere 
376f3c22059SEtienne Carriere 	return rc;
377f3c22059SEtienne Carriere }
378f3c22059SEtienne Carriere 
379cd451498SEtienne Carriere #ifdef CFG_WITH_SOFTWARE_PRNG
380cd451498SEtienne Carriere /* Override weak plat_rng_init with platform handler to seed PRNG */
381cd451498SEtienne Carriere void plat_rng_init(void)
382cd451498SEtienne Carriere {
383cd451498SEtienne Carriere 	uint8_t seed[RNG_FIFO_BYTE_DEPTH] = { };
384cd451498SEtienne Carriere 
385cd451498SEtienne Carriere 	if (stm32_rng_read(seed, sizeof(seed)))
386cd451498SEtienne Carriere 		panic();
387cd451498SEtienne Carriere 
388cd451498SEtienne Carriere 	if (crypto_rng_init(seed, sizeof(seed)))
389cd451498SEtienne Carriere 		panic();
390cd451498SEtienne Carriere 
391cd451498SEtienne Carriere 	DMSG("PRNG seeded with RNG");
392cd451498SEtienne Carriere }
393cd451498SEtienne Carriere #else
394cb2478efSAndrew Davis TEE_Result hw_get_random_bytes(void *out, size_t size)
395097f329aSEtienne Carriere {
396097f329aSEtienne Carriere 	return stm32_rng_read(out, size);
397097f329aSEtienne Carriere }
39827f3087bSGatien Chevallier 
39927f3087bSGatien Chevallier void plat_rng_init(void)
40027f3087bSGatien Chevallier {
40127f3087bSGatien Chevallier }
402097f329aSEtienne Carriere #endif
403097f329aSEtienne Carriere 
40429893549SGatien Chevallier static TEE_Result stm32_rng_pm_resume(uint32_t pm_cr)
40529893549SGatien Chevallier {
40629893549SGatien Chevallier 	vaddr_t base = get_base();
40729893549SGatien Chevallier 
40829893549SGatien Chevallier 	/* Clean error indications */
40929893549SGatien Chevallier 	io_write32(base + RNG_SR, 0);
41029893549SGatien Chevallier 
41129893549SGatien Chevallier 	if (stm32_rng->ddata->has_cond_reset) {
412eb5cf770SGatien Chevallier 		uint64_t timeout_ref = 0;
413eb5cf770SGatien Chevallier 
41429893549SGatien Chevallier 		/*
415c2c5b4beSGatien Chevallier 		 * Configuration must be set in the same access that sets
416c2c5b4beSGatien Chevallier 		 * RNG_CR_CONDRST bit. Otherwise, the configuration setting is
417c2c5b4beSGatien Chevallier 		 * not taken into account. CONFIGLOCK bit is always cleared in
418c2c5b4beSGatien Chevallier 		 * this configuration.
41929893549SGatien Chevallier 		 */
42029893549SGatien Chevallier 		io_write32(base + RNG_CR, pm_cr | RNG_CR_CONDRST);
42129893549SGatien Chevallier 
42229893549SGatien Chevallier 		io_clrsetbits32(base + RNG_CR, RNG_CR_CONDRST, RNG_CR_RNGEN);
423eb5cf770SGatien Chevallier 
424eb5cf770SGatien Chevallier 		timeout_ref = timeout_init_us(RNG_READY_TIMEOUT_US);
425eb5cf770SGatien Chevallier 		while (io_read32(base + RNG_CR) & RNG_CR_CONDRST)
426eb5cf770SGatien Chevallier 			if (timeout_elapsed(timeout_ref))
427eb5cf770SGatien Chevallier 				break;
428eb5cf770SGatien Chevallier 		if (io_read32(base + RNG_CR) & RNG_CR_CONDRST)
429eb5cf770SGatien Chevallier 			panic();
43029893549SGatien Chevallier 	} else {
43129893549SGatien Chevallier 		io_write32(base + RNG_CR, RNG_CR_RNGEN | pm_cr);
43229893549SGatien Chevallier 	}
43329893549SGatien Chevallier 
43429893549SGatien Chevallier 	return TEE_SUCCESS;
43529893549SGatien Chevallier }
43629893549SGatien Chevallier 
43729893549SGatien Chevallier static TEE_Result
43829893549SGatien Chevallier stm32_rng_pm(enum pm_op op, unsigned int pm_hint __unused,
43929893549SGatien Chevallier 	     const struct pm_callback_handle *pm_handle __unused)
44029893549SGatien Chevallier {
44129893549SGatien Chevallier 	static uint32_t pm_cr;
44229893549SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
44329893549SGatien Chevallier 
44429893549SGatien Chevallier 	assert(stm32_rng && (op == PM_OP_SUSPEND || op == PM_OP_RESUME));
44529893549SGatien Chevallier 
44629893549SGatien Chevallier 	res = clk_enable(stm32_rng->clock);
44729893549SGatien Chevallier 	if (res)
44829893549SGatien Chevallier 		return res;
44929893549SGatien Chevallier 
450*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock) {
451*45da6509SGatien Chevallier 		res = clk_enable(stm32_rng->bus_clock);
452*45da6509SGatien Chevallier 		if (res) {
453*45da6509SGatien Chevallier 			clk_disable(stm32_rng->clock);
454*45da6509SGatien Chevallier 			return res;
455*45da6509SGatien Chevallier 		}
456*45da6509SGatien Chevallier 	}
457*45da6509SGatien Chevallier 
45829893549SGatien Chevallier 	if (op == PM_OP_SUSPEND)
45929893549SGatien Chevallier 		pm_cr = io_read32(get_base() + RNG_CR);
46029893549SGatien Chevallier 	else
46129893549SGatien Chevallier 		res = stm32_rng_pm_resume(pm_cr);
46229893549SGatien Chevallier 
46329893549SGatien Chevallier 	clk_disable(stm32_rng->clock);
464*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock)
465*45da6509SGatien Chevallier 		clk_disable(stm32_rng->bus_clock);
46629893549SGatien Chevallier 
46729893549SGatien Chevallier 	return res;
46829893549SGatien Chevallier }
46929893549SGatien Chevallier DECLARE_KEEP_PAGER(stm32_rng_pm);
47029893549SGatien Chevallier 
471ea8ba295SGatien Chevallier static TEE_Result stm32_rng_parse_fdt(const void *fdt, int node)
472f3c22059SEtienne Carriere {
473d7a1a7d2SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
474ea8ba295SGatien Chevallier 	struct dt_node_info dt_rng = { };
475f3c22059SEtienne Carriere 
476f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &dt_rng, node);
477ea8ba295SGatien Chevallier 	if (dt_rng.reg == DT_INFO_INVALID_REG)
478ea8ba295SGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
479f3c22059SEtienne Carriere 
480ea8ba295SGatien Chevallier 	stm32_rng->base.pa = dt_rng.reg;
481ea8ba295SGatien Chevallier 	stm32_rng->base.va = io_pa_or_va_secure(&stm32_rng->base,
482ea8ba295SGatien Chevallier 						dt_rng.reg_size);
483ea8ba295SGatien Chevallier 	assert(stm32_rng->base.va);
484f3c22059SEtienne Carriere 
485ea8ba295SGatien Chevallier 	res = rstctrl_dt_get_by_index(fdt, node, 0, &stm32_rng->rstctrl);
486ea8ba295SGatien Chevallier 	if (res != TEE_SUCCESS && res != TEE_ERROR_ITEM_NOT_FOUND)
487ea8ba295SGatien Chevallier 		return res;
48868c4a16bSEtienne Carriere 
489*45da6509SGatien Chevallier 	if (stm32_rng->ddata->nb_clock > 1) {
490*45da6509SGatien Chevallier 		res = clk_dt_get_by_name(fdt, node, "rng_clk",
491*45da6509SGatien Chevallier 					 &stm32_rng->clock);
492*45da6509SGatien Chevallier 		if (res)
493*45da6509SGatien Chevallier 			return res;
494*45da6509SGatien Chevallier 
495*45da6509SGatien Chevallier 		res = clk_dt_get_by_name(fdt, node, "rng_hclk",
496*45da6509SGatien Chevallier 					 &stm32_rng->bus_clock);
497*45da6509SGatien Chevallier 		if (res)
498*45da6509SGatien Chevallier 			return res;
499*45da6509SGatien Chevallier 	} else {
500d7a1a7d2SEtienne Carriere 		res = clk_dt_get_by_index(fdt, node, 0, &stm32_rng->clock);
501d7a1a7d2SEtienne Carriere 		if (res)
502d7a1a7d2SEtienne Carriere 			return res;
503*45da6509SGatien Chevallier 	}
504d7a1a7d2SEtienne Carriere 
5053c752300SGatien Chevallier 	if (fdt_getprop(fdt, node, "clock-error-detect", NULL))
5063c752300SGatien Chevallier 		stm32_rng->clock_error = true;
5073c752300SGatien Chevallier 
508ea8ba295SGatien Chevallier 	/* Release device if not used at runtime or for pm transitions */
509ea8ba295SGatien Chevallier 	stm32_rng->release_post_boot = IS_ENABLED(CFG_WITH_SOFTWARE_PRNG) &&
510ea8ba295SGatien Chevallier 				       !IS_ENABLED(CFG_PM);
511f3c22059SEtienne Carriere 
512f3c22059SEtienne Carriere 	return TEE_SUCCESS;
513f3c22059SEtienne Carriere }
514f3c22059SEtienne Carriere 
515ea8ba295SGatien Chevallier static TEE_Result stm32_rng_probe(const void *fdt, int offs,
516f9508605SGatien Chevallier 				  const void *compat_data)
517ea8ba295SGatien Chevallier {
518ea8ba295SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
519ea8ba295SGatien Chevallier 
520ea8ba295SGatien Chevallier 	/* Expect a single RNG instance */
521ea8ba295SGatien Chevallier 	assert(!stm32_rng);
522ea8ba295SGatien Chevallier 
523ea8ba295SGatien Chevallier 	stm32_rng = calloc(1, sizeof(*stm32_rng));
524ea8ba295SGatien Chevallier 	if (!stm32_rng)
525ea8ba295SGatien Chevallier 		panic();
526ea8ba295SGatien Chevallier 
527ea8ba295SGatien Chevallier 	res = stm32_rng_parse_fdt(fdt, offs);
528ea8ba295SGatien Chevallier 	if (res)
529ea8ba295SGatien Chevallier 		goto err;
530ea8ba295SGatien Chevallier 
531091ef005SGatien Chevallier 	stm32_rng->ddata = compat_data;
532091ef005SGatien Chevallier 	assert(stm32_rng->ddata);
533091ef005SGatien Chevallier 
534ea8ba295SGatien Chevallier 	res = clk_enable(stm32_rng->clock);
535ea8ba295SGatien Chevallier 	if (res)
536ea8ba295SGatien Chevallier 		goto err;
537ea8ba295SGatien Chevallier 
538*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock) {
539*45da6509SGatien Chevallier 		res = clk_enable(stm32_rng->bus_clock);
540*45da6509SGatien Chevallier 		if (res) {
541*45da6509SGatien Chevallier 			clk_disable(stm32_rng->clock);
542*45da6509SGatien Chevallier 			goto err;
543*45da6509SGatien Chevallier 		}
544*45da6509SGatien Chevallier 	}
545*45da6509SGatien Chevallier 
546ea8ba295SGatien Chevallier 	if (stm32_rng->rstctrl &&
547ea8ba295SGatien Chevallier 	    rstctrl_assert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) {
548ea8ba295SGatien Chevallier 		res = TEE_ERROR_GENERIC;
549ea8ba295SGatien Chevallier 		goto err_clk;
550ea8ba295SGatien Chevallier 	}
551ea8ba295SGatien Chevallier 
552ea8ba295SGatien Chevallier 	if (stm32_rng->rstctrl &&
553ea8ba295SGatien Chevallier 	    rstctrl_deassert_to(stm32_rng->rstctrl, RNG_RESET_TIMEOUT_US)) {
554ea8ba295SGatien Chevallier 		res = TEE_ERROR_GENERIC;
555ea8ba295SGatien Chevallier 		goto err_clk;
556ea8ba295SGatien Chevallier 	}
557ea8ba295SGatien Chevallier 
558f63f11bdSGatien Chevallier 	res = init_rng();
559f63f11bdSGatien Chevallier 	if (res)
560f63f11bdSGatien Chevallier 		goto err_clk;
561f63f11bdSGatien Chevallier 
562ea8ba295SGatien Chevallier 	clk_disable(stm32_rng->clock);
563*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock)
564*45da6509SGatien Chevallier 		clk_disable(stm32_rng->bus_clock);
565ea8ba295SGatien Chevallier 
566ea8ba295SGatien Chevallier 	if (stm32_rng->release_post_boot)
567ea8ba295SGatien Chevallier 		stm32mp_register_non_secure_periph_iomem(stm32_rng->base.pa);
568ea8ba295SGatien Chevallier 	else
569ea8ba295SGatien Chevallier 		stm32mp_register_secure_periph_iomem(stm32_rng->base.pa);
570ea8ba295SGatien Chevallier 
57129893549SGatien Chevallier 	register_pm_core_service_cb(stm32_rng_pm, &stm32_rng, "rng-service");
57229893549SGatien Chevallier 
573ea8ba295SGatien Chevallier 	return TEE_SUCCESS;
574ea8ba295SGatien Chevallier 
575ea8ba295SGatien Chevallier err_clk:
576ea8ba295SGatien Chevallier 	clk_disable(stm32_rng->clock);
577*45da6509SGatien Chevallier 	if (stm32_rng->bus_clock)
578*45da6509SGatien Chevallier 		clk_disable(stm32_rng->bus_clock);
579ea8ba295SGatien Chevallier err:
580ea8ba295SGatien Chevallier 	free(stm32_rng);
581ea8ba295SGatien Chevallier 	stm32_rng = NULL;
582ea8ba295SGatien Chevallier 
583ea8ba295SGatien Chevallier 	return res;
584ea8ba295SGatien Chevallier }
585ea8ba295SGatien Chevallier 
586091ef005SGatien Chevallier static const struct stm32_rng_driver_data mp13_data[] = {
587*45da6509SGatien Chevallier 	{
588*45da6509SGatien Chevallier 		.nb_clock = 1,
589*45da6509SGatien Chevallier 		.has_cond_reset = true,
590*45da6509SGatien Chevallier 	},
591091ef005SGatien Chevallier };
592091ef005SGatien Chevallier 
593091ef005SGatien Chevallier static const struct stm32_rng_driver_data mp15_data[] = {
594*45da6509SGatien Chevallier 	{
595*45da6509SGatien Chevallier 		.nb_clock = 1,
596*45da6509SGatien Chevallier 		.has_cond_reset = false,
597*45da6509SGatien Chevallier 	},
598091ef005SGatien Chevallier };
599091ef005SGatien Chevallier DECLARE_KEEP_PAGER(mp15_data);
600091ef005SGatien Chevallier 
601*45da6509SGatien Chevallier static const struct stm32_rng_driver_data mp25_data[] = {
602*45da6509SGatien Chevallier 	{
603*45da6509SGatien Chevallier 		.nb_clock = 2,
604*45da6509SGatien Chevallier 		.has_cond_reset = true,
605*45da6509SGatien Chevallier 	},
606*45da6509SGatien Chevallier };
607*45da6509SGatien Chevallier 
608ea8ba295SGatien Chevallier static const struct dt_device_match rng_match_table[] = {
609091ef005SGatien Chevallier 	{ .compatible = "st,stm32-rng", .compat_data = &mp15_data },
610091ef005SGatien Chevallier 	{ .compatible = "st,stm32mp13-rng", .compat_data = &mp13_data },
611*45da6509SGatien Chevallier 	{ .compatible = "st,stm32mp25-rng", .compat_data = &mp25_data },
612ea8ba295SGatien Chevallier 	{ }
613ea8ba295SGatien Chevallier };
614ea8ba295SGatien Chevallier 
615ea8ba295SGatien Chevallier DEFINE_DT_DRIVER(stm32_rng_dt_driver) = {
616ea8ba295SGatien Chevallier 	.name = "stm32_rng",
617ea8ba295SGatien Chevallier 	.match_table = rng_match_table,
618ea8ba295SGatien Chevallier 	.probe = stm32_rng_probe,
619ea8ba295SGatien Chevallier };
620d8682c4cSEtienne Carriere 
621d8682c4cSEtienne Carriere static TEE_Result stm32_rng_release(void)
622d8682c4cSEtienne Carriere {
623d8682c4cSEtienne Carriere 	if (stm32_rng && stm32_rng->release_post_boot) {
624d8682c4cSEtienne Carriere 		DMSG("Release RNG driver");
625d8682c4cSEtienne Carriere 		free(stm32_rng);
626d8682c4cSEtienne Carriere 		stm32_rng = NULL;
627d8682c4cSEtienne Carriere 	}
628d8682c4cSEtienne Carriere 
629d8682c4cSEtienne Carriere 	return TEE_SUCCESS;
630d8682c4cSEtienne Carriere }
631d8682c4cSEtienne Carriere 
632d8682c4cSEtienne Carriere release_init_resource(stm32_rng_release);
633