1 /*
2  * Copyright (c) 2020 Lemonbeat GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT silabs_gecko_trng
8 
9  #include <zephyr/drivers/entropy.h>
10  #include <string.h>
11  #include "soc.h"
12  #include "em_cmu.h"
13 
14 #if defined(CONFIG_CRYPTO_ACC_GECKO_TRNG)
15 
16 /*
17  * Select the correct Crypto ACC FIFO memory base address.
18  *
19  * Problem: Gecko SDK doesn't provide macros that check if SL_TRUSTZONE is used or not for Crypto
20  * ACC RNGOUT FIFO memory base address, like it does for register address definitions.
21  *
22  * Solution: Check which register base address is used for the Crypto ACC peripheral and select an
23  * appropriate FIFO memory base address.
24  */
25 #if (CRYPTOACC_BASE == CRYPTOACC_S_BASE)
26 #define S2_FIFO_BASE CRYPTOACC_RNGOUT_FIFO_S_MEM_BASE
27 #else
28 #define S2_FIFO_BASE CRYPTOACC_RNGOUT_FIFO_MEM_BASE
29 #endif
30 
31 /**
32  * Series 2 SoCs have different TRNG register definitions
33  */
34 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)	/* xG22 */
35 #define S2_FIFO_LEVEL	(CRYPTOACC_RNGCTRL->FIFOLEVEL)
36 #define S2_CTRL		(CRYPTOACC_RNGCTRL->RNGCTRL)
37 #define S2_CTRL_ENABLE	(CRYPTOACC_RNGCTRL_ENABLE)
38 #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)	/* xG27 */
39 #define S2_FIFO_LEVEL	(CRYPTOACC->NDRNG_FIFOLEVEL)
40 #define S2_CTRL		(CRYPTOACC->NDRNG_CONTROL)
41 #define S2_CTRL_ENABLE	(CRYPTOACC_NDRNG_CONTROL_ENABLE)
42 #else	/* _SILICON_LABS_32B_SERIES_2_CONFIG_* */
43 #error "Building for unsupported Series 2 SoC"
44 #endif	/* _SILICON_LABS_32B_SERIES_2_CONFIG_* */
45 #endif	/* CONFIG_CRYPTO_ACC_GECKO_TRNG */
46 
entropy_gecko_trng_read(uint8_t * output,size_t len)47 static void entropy_gecko_trng_read(uint8_t *output, size_t len)
48 {
49 #ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
50 	uint32_t tmp;
51 	uint32_t *data = (uint32_t *) output;
52 
53 	/* Read known good available data. */
54 	while (len >= 4) {
55 		*data++ = TRNG0->FIFO;
56 		len -= 4;
57 	}
58 	if (len > 0) {
59 		/* Handle the case where len is not a multiple of 4
60 		 * and FIFO data is available.
61 		 */
62 		tmp = TRNG0->FIFO;
63 		memcpy(data, (const uint8_t *) &tmp, len);
64 	}
65 #else
66 	memcpy(output, ((const uint8_t *) S2_FIFO_BASE), len);
67 #endif
68 }
69 
entropy_gecko_trng_get_entropy(const struct device * dev,uint8_t * buffer,uint16_t length)70 static int entropy_gecko_trng_get_entropy(const struct device *dev,
71 					  uint8_t *buffer,
72 					  uint16_t length)
73 {
74 	size_t count = 0;
75 	size_t available;
76 
77 	ARG_UNUSED(dev);
78 
79 	while (length) {
80 #ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
81 		available = TRNG0->FIFOLEVEL * 4;
82 #else
83 		available = S2_FIFO_LEVEL * 4;
84 #endif
85 		if (available == 0) {
86 			return -EINVAL;
87 		}
88 
89 		count = SL_MIN(length, available);
90 		entropy_gecko_trng_read(buffer, count);
91 		buffer += count;
92 		length -= count;
93 	}
94 
95 	return 0;
96 }
97 
entropy_gecko_trng_get_entropy_isr(const struct device * dev,uint8_t * buf,uint16_t len,uint32_t flags)98 static int entropy_gecko_trng_get_entropy_isr(const struct device *dev,
99 					      uint8_t *buf,
100 					      uint16_t len, uint32_t flags)
101 {
102 
103 	if ((flags & ENTROPY_BUSYWAIT) == 0U) {
104 
105 		/* No busy wait; return whatever data is available. */
106 		size_t count;
107 #ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
108 		size_t available = TRNG0->FIFOLEVEL * 4;
109 #else
110 		size_t available = S2_FIFO_LEVEL * 4;
111 #endif
112 
113 		if (available == 0) {
114 			return -ENODATA;
115 		}
116 		count = SL_MIN(len, available);
117 		entropy_gecko_trng_read(buf, count);
118 		return count;
119 
120 	} else {
121 		/* Allowed to busy-wait */
122 		int ret = entropy_gecko_trng_get_entropy(dev, buf, len);
123 
124 		if (ret == 0) {
125 			/* Data retrieved successfully. */
126 			return len;
127 		}
128 		return ret;
129 	}
130 }
131 
entropy_gecko_trng_init(const struct device * dev)132 static int entropy_gecko_trng_init(const struct device *dev)
133 {
134 	/* Enable the TRNG0 clock. */
135 #ifndef CONFIG_CRYPTO_ACC_GECKO_TRNG
136 	CMU_ClockEnable(cmuClock_TRNG0, true);
137 
138 	/* Enable TRNG0. */
139 	TRNG0->CONTROL = TRNG_CONTROL_ENABLE;
140 #else
141 	/* Enable the CRYPTO ACC clock. */
142 	CMU_ClockEnable(cmuClock_CRYPTOACC, true);
143 
144 	/* Enable TRNG */
145 	S2_CTRL |= S2_CTRL_ENABLE;
146 #endif
147 
148 	return 0;
149 }
150 
151 static struct entropy_driver_api entropy_gecko_trng_api_funcs = {
152 	.get_entropy = entropy_gecko_trng_get_entropy,
153 	.get_entropy_isr = entropy_gecko_trng_get_entropy_isr
154 };
155 
156 DEVICE_DT_INST_DEFINE(0,
157 			entropy_gecko_trng_init, NULL,
158 			NULL, NULL,
159 			PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
160 			&entropy_gecko_trng_api_funcs);
161