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