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