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)42 static int entropy_sam_wait_ready(Trng * const trng)
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 
61 	return 0;
62 }
63 
entropy_sam_get_entropy_internal(const struct device * dev,uint8_t * buffer,uint16_t length)64 static int entropy_sam_get_entropy_internal(const struct device *dev,
65 					    uint8_t *buffer,
66 					    uint16_t length)
67 {
68 	const struct trng_sam_dev_cfg *config = dev->config;
69 	Trng *const trng = config->regs;
70 
71 	while (length > 0) {
72 		size_t to_copy;
73 		uint32_t value;
74 		int res;
75 
76 		res = entropy_sam_wait_ready(trng);
77 		if (res < 0) {
78 			return res;
79 		}
80 
81 		value = _data(trng);
82 		to_copy = MIN(length, sizeof(value));
83 
84 		memcpy(buffer, &value, to_copy);
85 		buffer += to_copy;
86 		length -= to_copy;
87 	}
88 
89 	return 0;
90 }
91 
entropy_sam_get_entropy(const struct device * dev,uint8_t * buffer,uint16_t length)92 static int entropy_sam_get_entropy(const struct device *dev, uint8_t *buffer,
93 				   uint16_t length)
94 {
95 	return entropy_sam_get_entropy_internal(dev, buffer, length);
96 }
97 
entropy_sam_get_entropy_isr(const struct device * dev,uint8_t * buffer,uint16_t length,uint32_t flags)98 static int entropy_sam_get_entropy_isr(const struct device *dev,
99 				       uint8_t *buffer,
100 				       uint16_t length, uint32_t flags)
101 {
102 	uint16_t cnt = length;
103 
104 
105 	if ((flags & ENTROPY_BUSYWAIT) == 0U) {
106 		const struct trng_sam_dev_cfg *config = dev->config;
107 		/* No busy wait; return whatever data is available. */
108 
109 		Trng * const trng = config->regs;
110 
111 		do {
112 			size_t to_copy;
113 			uint32_t value;
114 
115 			if (!_ready(trng)) {
116 
117 				/* Data not ready */
118 				break;
119 			}
120 
121 			value = _data(trng);
122 			to_copy = MIN(length, sizeof(value));
123 
124 			memcpy(buffer, &value, to_copy);
125 			buffer += to_copy;
126 			length -= to_copy;
127 
128 		} while (length > 0);
129 
130 		return cnt - length;
131 
132 	} else {
133 		/* Allowed to busy-wait */
134 		int ret =
135 			entropy_sam_get_entropy_internal(dev,
136 				buffer, length);
137 
138 		if (ret == 0) {
139 			/* Data retrieved successfully. */
140 			return cnt;
141 		}
142 
143 		return ret;
144 	}
145 }
146 
entropy_sam_init(const struct device * dev)147 static int entropy_sam_init(const struct device *dev)
148 {
149 	const struct trng_sam_dev_cfg *config = dev->config;
150 	Trng *const trng = config->regs;
151 
152 #ifdef MCLK
153 	/* Enable the MCLK */
154 	MCLK->APBCMASK.bit.TRNG_ = 1;
155 
156 	/* Enable the TRNG */
157 	trng->CTRLA.bit.ENABLE = 1;
158 #else
159 	/* Enable TRNG in PMC */
160 	const struct atmel_sam_pmc_config clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(0);
161 	(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
162 			       (clock_control_subsys_t)&clock_cfg);
163 
164 	/* Enable the TRNG */
165 	trng->TRNG_CR = TRNG_CR_KEY_PASSWD | TRNG_CR_ENABLE;
166 #endif
167 	return 0;
168 }
169 
170 static const struct entropy_driver_api entropy_sam_api = {
171 	.get_entropy = entropy_sam_get_entropy,
172 	.get_entropy_isr = entropy_sam_get_entropy_isr
173 };
174 
175 static const struct trng_sam_dev_cfg trng_sam_cfg = {
176 	.regs = (Trng *)DT_INST_REG_ADDR(0),
177 };
178 
179 DEVICE_DT_INST_DEFINE(0,
180 		    entropy_sam_init, NULL,
181 		    NULL, &trng_sam_cfg,
182 		    PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
183 		    &entropy_sam_api);
184