1 /*
2  * Copyright (c) 2018 Aurelien Jarno
3  * Copyright (c) 2023 Gerson Fernando Budke
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT atmel_sam_trng
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/entropy.h>
12 #include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
13 #include <errno.h>
14 #include <zephyr/init.h>
15 #include <zephyr/kernel.h>
16 #include <soc.h>
17 #include <string.h>
18 
19 struct trng_sam_dev_cfg {
20 	Trng *regs;
21 };
22 
_ready(Trng * const trng)23 static inline bool _ready(Trng * const trng)
24 {
25 #ifdef TRNG_ISR_DATRDY
26 	return trng->TRNG_ISR & TRNG_ISR_DATRDY;
27 #else
28 	return trng->INTFLAG.bit.DATARDY;
29 #endif
30 }
31 
_data(Trng * const trng)32 static inline uint32_t _data(Trng * const trng)
33 {
34 #ifdef REG_TRNG_DATA
35 	(void) trng;
36 	return TRNG->DATA.reg;
37 #else
38 	return trng->TRNG_ODATA;
39 #endif
40 }
41 
entropy_sam_wait_ready(Trng * const trng,uint32_t flags)42 static int entropy_sam_wait_ready(Trng * const trng, uint32_t flags)
43 {
44 	/* According to the reference manual, the generator provides
45 	 * one 32-bit random value every 84 peripheral clock cycles.
46 	 * MCK may not be smaller than HCLK/4, so it should not take
47 	 * more than 336 HCLK ticks. Assuming the CPU can do 1
48 	 * instruction per HCLK the number of times to loop before
49 	 * the TRNG is ready is less than 1000. And that is when
50 	 * assuming the loop only takes 1 instruction. So looping a
51 	 * million times should be more than enough.
52 	 */
53 	int timeout = 1000000;
54 
55 	while (!_ready(trng)) {
56 		if (timeout-- == 0) {
57 			return -ETIMEDOUT;
58 		}
59 
60 		if ((flags & ENTROPY_BUSYWAIT) == 0U) {
61 			/* This internal function is used by both get_entropy,
62 			 * and get_entropy_isr APIs. The later may call this
63 			 * function with the ENTROPY_BUSYWAIT flag set. In
64 			 * that case make no assumption that the kernel is
65 			 * initialized when the function is called; so, just
66 			 * do busy-wait for the random data to be ready.
67 			 */
68 			k_yield();
69 		}
70 	}
71 
72 	return 0;
73 }
74 
entropy_sam_get_entropy_internal(const struct device * dev,uint8_t * buffer,uint16_t length,uint32_t flags)75 static int entropy_sam_get_entropy_internal(const struct device *dev,
76 					    uint8_t *buffer,
77 					    uint16_t length, uint32_t flags)
78 {
79 	const struct trng_sam_dev_cfg *config = dev->config;
80 	Trng *const trng = config->regs;
81 
82 	while (length > 0) {
83 		size_t to_copy;
84 		uint32_t value;
85 		int res;
86 
87 		res = entropy_sam_wait_ready(trng, flags);
88 		if (res < 0) {
89 			return res;
90 		}
91 
92 		value = _data(trng);
93 		to_copy = MIN(length, sizeof(value));
94 
95 		memcpy(buffer, &value, to_copy);
96 		buffer += to_copy;
97 		length -= to_copy;
98 	}
99 
100 	return 0;
101 }
102 
entropy_sam_get_entropy(const struct device * dev,uint8_t * buffer,uint16_t length)103 static int entropy_sam_get_entropy(const struct device *dev, uint8_t *buffer,
104 				   uint16_t length)
105 {
106 	return entropy_sam_get_entropy_internal(dev, buffer, length, 0);
107 }
108 
entropy_sam_get_entropy_isr(const struct device * dev,uint8_t * buffer,uint16_t length,uint32_t flags)109 static int entropy_sam_get_entropy_isr(const struct device *dev,
110 				       uint8_t *buffer,
111 				       uint16_t length, uint32_t flags)
112 {
113 	uint16_t cnt = length;
114 
115 
116 	if ((flags & ENTROPY_BUSYWAIT) == 0U) {
117 		const struct trng_sam_dev_cfg *config = dev->config;
118 		/* No busy wait; return whatever data is available. */
119 
120 		Trng * const trng = config->regs;
121 
122 		do {
123 			size_t to_copy;
124 			uint32_t value;
125 
126 			if (!_ready(trng)) {
127 
128 				/* Data not ready */
129 				break;
130 			}
131 
132 			value = _data(trng);
133 			to_copy = MIN(length, sizeof(value));
134 
135 			memcpy(buffer, &value, to_copy);
136 			buffer += to_copy;
137 			length -= to_copy;
138 
139 		} while (length > 0);
140 
141 		return cnt - length;
142 
143 	} else {
144 		/* Allowed to busy-wait */
145 		int ret =
146 			entropy_sam_get_entropy_internal(dev,
147 				buffer, length, flags);
148 
149 		if (ret == 0) {
150 			/* Data retrieved successfully. */
151 			return cnt;
152 		}
153 
154 		return ret;
155 	}
156 }
157 
entropy_sam_init(const struct device * dev)158 static int entropy_sam_init(const struct device *dev)
159 {
160 	const struct trng_sam_dev_cfg *config = dev->config;
161 	Trng *const trng = config->regs;
162 
163 #ifdef MCLK
164 	/* Enable the MCLK */
165 	MCLK->APBCMASK.bit.TRNG_ = 1;
166 
167 	/* Enable the TRNG */
168 	trng->CTRLA.bit.ENABLE = 1;
169 #else
170 	/* Enable TRNG in PMC */
171 	const struct atmel_sam_pmc_config clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(0);
172 	(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
173 			       (clock_control_subsys_t)&clock_cfg);
174 
175 	/* Enable the TRNG */
176 	trng->TRNG_CR = TRNG_CR_KEY_PASSWD | TRNG_CR_ENABLE;
177 #endif
178 	return 0;
179 }
180 
181 static const struct entropy_driver_api entropy_sam_api = {
182 	.get_entropy = entropy_sam_get_entropy,
183 	.get_entropy_isr = entropy_sam_get_entropy_isr
184 };
185 
186 static const struct trng_sam_dev_cfg trng_sam_cfg = {
187 	.regs = (Trng *)DT_INST_REG_ADDR(0),
188 };
189 
190 DEVICE_DT_INST_DEFINE(0,
191 		    entropy_sam_init, NULL,
192 		    NULL, &trng_sam_cfg,
193 		    PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
194 		    &entropy_sam_api);
195