xref: /rk3399_ARM-atf/drivers/rpi3/rng/rpi3_rng.c (revision 5318255f12f88c91846b7261ce12254fb8395557)
1990ab78eSAndre Przywara /*
2*7a9cdf58SMario Bălănică  * Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved.
3990ab78eSAndre Przywara  *
4990ab78eSAndre Przywara  * SPDX-License-Identifier: BSD-3-Clause
5990ab78eSAndre Przywara  */
6990ab78eSAndre Przywara 
7990ab78eSAndre Przywara #include <assert.h>
8990ab78eSAndre Przywara #include <string.h>
9990ab78eSAndre Przywara 
10990ab78eSAndre Przywara #include <lib/mmio.h>
11990ab78eSAndre Przywara 
12990ab78eSAndre Przywara #include <rpi_hw.h>
13990ab78eSAndre Przywara 
14*7a9cdf58SMario Bălănică #define RPI3_RNG_CTRL_OFFSET		ULL(0x00000000)
15*7a9cdf58SMario Bălănică #define RPI3_RNG_STATUS_OFFSET		ULL(0x00000004)
16*7a9cdf58SMario Bălănică #define RPI3_RNG_DATA_OFFSET		ULL(0x00000008)
17*7a9cdf58SMario Bălănică #define RPI3_RNG_INT_MASK_OFFSET	ULL(0x00000010)
18*7a9cdf58SMario Bălănică /* Enable/disable RNG */
19*7a9cdf58SMario Bălănică #define RPI3_RNG_CTRL_ENABLE		U(0x1)
20*7a9cdf58SMario Bălănică #define RPI3_RNG_CTRL_DISABLE		U(0x0)
21*7a9cdf58SMario Bălănică /* Number of currently available words */
22*7a9cdf58SMario Bălănică #define RPI3_RNG_STATUS_NUM_WORDS_SHIFT	U(24)
23*7a9cdf58SMario Bălănică #define RPI3_RNG_STATUS_NUM_WORDS_MASK	U(0xFF)
24*7a9cdf58SMario Bălănică /* Value to mask interrupts caused by the RNG */
25*7a9cdf58SMario Bălănică #define RPI3_RNG_INT_MASK_DISABLE	U(0x1)
26*7a9cdf58SMario Bălănică 
27990ab78eSAndre Przywara /* Initial amount of values to discard */
28990ab78eSAndre Przywara #define RNG_WARMUP_COUNT	U(0x40000)
29990ab78eSAndre Przywara 
rpi3_rng_initialize(void)30990ab78eSAndre Przywara static void rpi3_rng_initialize(void)
31990ab78eSAndre Przywara {
32990ab78eSAndre Przywara 	uint32_t int_mask, ctrl;
33990ab78eSAndre Przywara 
34990ab78eSAndre Przywara 	/* Return if it is already enabled */
35990ab78eSAndre Przywara 	ctrl = mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_CTRL_OFFSET);
36990ab78eSAndre Przywara 	if ((ctrl & RPI3_RNG_CTRL_ENABLE) != 0U) {
37990ab78eSAndre Przywara 		return;
38990ab78eSAndre Przywara 	}
39990ab78eSAndre Przywara 
40990ab78eSAndre Przywara 	/* Mask interrupts */
41990ab78eSAndre Przywara 	int_mask = mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_INT_MASK_OFFSET);
42990ab78eSAndre Przywara 	int_mask |= RPI3_RNG_INT_MASK_DISABLE;
43990ab78eSAndre Przywara 	mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_INT_MASK_OFFSET, int_mask);
44990ab78eSAndre Przywara 
45990ab78eSAndre Przywara 	/* Discard several values when initializing to give it time to warmup */
46990ab78eSAndre Przywara 	mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_STATUS_OFFSET, RNG_WARMUP_COUNT);
47990ab78eSAndre Przywara 
48990ab78eSAndre Przywara 	mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_CTRL_OFFSET,
49990ab78eSAndre Przywara 		      RPI3_RNG_CTRL_ENABLE);
50990ab78eSAndre Przywara }
51990ab78eSAndre Przywara 
rpi3_rng_get_word(void)52990ab78eSAndre Przywara static uint32_t rpi3_rng_get_word(void)
53990ab78eSAndre Przywara {
54990ab78eSAndre Przywara 	size_t nwords;
55990ab78eSAndre Przywara 
56990ab78eSAndre Przywara 	do {
57990ab78eSAndre Przywara 		/* Get number of available words to read */
58990ab78eSAndre Przywara 		nwords = (mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_STATUS_OFFSET)
59990ab78eSAndre Przywara 				       >> RPI3_RNG_STATUS_NUM_WORDS_SHIFT)
60990ab78eSAndre Przywara 				       & RPI3_RNG_STATUS_NUM_WORDS_MASK;
61990ab78eSAndre Przywara 	} while (nwords == 0U);
62990ab78eSAndre Przywara 
63990ab78eSAndre Przywara 	return mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_DATA_OFFSET);
64990ab78eSAndre Przywara }
65990ab78eSAndre Przywara 
rpi3_rng_read(void * buf,size_t len)66990ab78eSAndre Przywara void rpi3_rng_read(void *buf, size_t len)
67990ab78eSAndre Przywara {
68990ab78eSAndre Przywara 	uint32_t data;
69990ab78eSAndre Przywara 	size_t left = len;
70990ab78eSAndre Przywara 	uint32_t *dst = buf;
71990ab78eSAndre Przywara 
72990ab78eSAndre Przywara 	assert(buf != NULL);
73990ab78eSAndre Przywara 	assert(len != 0U);
74990ab78eSAndre Przywara 	assert(check_uptr_overflow((uintptr_t) buf, (uintptr_t) len) == 0);
75990ab78eSAndre Przywara 
76990ab78eSAndre Przywara 	rpi3_rng_initialize();
77990ab78eSAndre Przywara 
78990ab78eSAndre Przywara 	while (left >= sizeof(uint32_t)) {
79990ab78eSAndre Przywara 		data = rpi3_rng_get_word();
80990ab78eSAndre Przywara 		*dst++ = data;
81990ab78eSAndre Przywara 		left -= sizeof(uint32_t);
82990ab78eSAndre Przywara 	}
83990ab78eSAndre Przywara 
84990ab78eSAndre Przywara 	if (left > 0U) {
85990ab78eSAndre Przywara 		data = rpi3_rng_get_word();
86990ab78eSAndre Przywara 		memcpy(dst, &data, left);
87990ab78eSAndre Przywara 	}
88990ab78eSAndre Przywara }
89