1 /*
2 * Copyright (c) 2019 Brett Witherspoon
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ti_cc13xx_cc26xx_trng
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/entropy.h>
12 #include <zephyr/irq.h>
13 #include <zephyr/pm/policy.h>
14 #include <zephyr/pm/device.h>
15
16 #include <zephyr/sys/ring_buffer.h>
17 #include <zephyr/sys/sys_io.h>
18
19 #include <driverlib/prcm.h>
20 #include <driverlib/trng.h>
21
22 #include <ti/drivers/Power.h>
23 #include <ti/drivers/power/PowerCC26X2.h>
24
25 #define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
26
27 #define US_PER_SAMPLE (1000000ULL * \
28 CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE / CPU_FREQ + 1ULL)
29
30 struct entropy_cc13xx_cc26xx_data {
31 struct k_sem lock;
32 struct k_sem sync;
33 struct ring_buf pool;
34 uint8_t data[CONFIG_ENTROPY_CC13XX_CC26XX_POOL_SIZE];
35 #ifdef CONFIG_PM
36 Power_NotifyObj post_notify;
37 bool constrained;
38 #endif
39 };
40
start_trng(struct entropy_cc13xx_cc26xx_data * data)41 static void start_trng(struct entropy_cc13xx_cc26xx_data *data)
42 {
43 /* Initialization as described in TRM section 18.6.1.2 */
44 TRNGReset();
45 while (sys_read32(TRNG_BASE + TRNG_O_SWRESET)) {
46 continue;
47 }
48
49 /* Set samples per cycle */
50 TRNGConfigure(0, CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE,
51 0);
52 /* De-tune FROs */
53 sys_write32(TRNG_FRODETUNE_FRO_MASK_M, TRNG_BASE +
54 TRNG_O_FRODETUNE);
55 /* Enable FROs */
56 sys_write32(TRNG_FROEN_FRO_MASK_M, TRNG_BASE + TRNG_O_FROEN);
57 /* Set shutdown and alarm thresholds */
58 sys_write32((CONFIG_ENTROPY_CC13XX_CC26XX_SHUTDOWN_THRESHOLD
59 << 16) | CONFIG_ENTROPY_CC13XX_CC26XX_ALARM_THRESHOLD,
60 TRNG_BASE + TRNG_O_ALARMCNT);
61
62 TRNGEnable();
63 TRNGIntEnable(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
64 }
65
66 #ifdef CONFIG_PM_DEVICE
stop_trng(struct entropy_cc13xx_cc26xx_data * data)67 static void stop_trng(struct entropy_cc13xx_cc26xx_data *data)
68 {
69 TRNGDisable();
70
71 TRNGIntClear(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
72 TRNGIntDisable(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
73 }
74 #endif
75
handle_shutdown_ovf(void)76 static void handle_shutdown_ovf(void)
77 {
78 uint32_t off;
79
80 /* Clear shutdown */
81 TRNGIntClear(TRNG_FRO_SHUTDOWN);
82 /* Disabled FROs */
83 off = sys_read32(TRNG_BASE + TRNG_O_ALARMSTOP);
84 /* Clear alarms */
85 sys_write32(0, TRNG_BASE + TRNG_O_ALARMMASK);
86 sys_write32(0, TRNG_BASE + TRNG_O_ALARMSTOP);
87 /* De-tune FROs */
88 sys_write32(off, TRNG_BASE + TRNG_O_FRODETUNE);
89 /* Re-enable FROs */
90 sys_write32(off, TRNG_BASE + TRNG_O_FROEN);
91 }
92
entropy_cc13xx_cc26xx_get_entropy(const struct device * dev,uint8_t * buf,uint16_t len)93 static int entropy_cc13xx_cc26xx_get_entropy(const struct device *dev,
94 uint8_t *buf,
95 uint16_t len)
96 {
97 struct entropy_cc13xx_cc26xx_data *data = dev->data;
98 uint32_t cnt;
99
100 #ifdef CONFIG_PM
101 unsigned int key = irq_lock();
102
103 if (!data->constrained) {
104 pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
105 data->constrained = true;
106 }
107 irq_unlock(key);
108 #endif
109
110 TRNGIntEnable(TRNG_NUMBER_READY);
111
112 while (len) {
113 k_sem_take(&data->lock, K_FOREVER);
114 cnt = ring_buf_get(&data->pool, buf, len);
115 k_sem_give(&data->lock);
116
117 if (cnt) {
118 buf += cnt;
119 len -= cnt;
120 } else {
121 k_sem_take(&data->sync, K_FOREVER);
122 }
123 }
124
125 return 0;
126 }
127
entropy_cc13xx_cc26xx_isr(const struct device * dev)128 static void entropy_cc13xx_cc26xx_isr(const struct device *dev)
129 {
130 struct entropy_cc13xx_cc26xx_data *data = dev->data;
131 uint32_t src = 0;
132 uint32_t cnt;
133 uint32_t num[2];
134
135 /* Interrupt service routine as described in TRM section 18.6.1.3.2 */
136 src = TRNGStatusGet();
137
138 if (src & TRNG_NUMBER_READY) {
139 /* This function acknowledges the ready status */
140 num[1] = TRNGNumberGet(TRNG_HI_WORD);
141 num[0] = TRNGNumberGet(TRNG_LOW_WORD);
142
143 cnt = ring_buf_put(&data->pool, (uint8_t *)num, sizeof(num));
144
145 /* When pool is full disable interrupt and stop reading numbers */
146 if (cnt != sizeof(num)) {
147 #ifdef CONFIG_PM
148 if (data->constrained) {
149 pm_policy_state_lock_put(
150 PM_STATE_STANDBY,
151 PM_ALL_SUBSTATES);
152 data->constrained = false;
153 }
154 #endif
155 TRNGIntDisable(TRNG_NUMBER_READY);
156 }
157
158 k_sem_give(&data->sync);
159 }
160
161 /* Change the shutdown FROs' oscillating frequency in an attempt to
162 * prevent further locking on to the sampling clock frequency.
163 */
164 if (src & TRNG_FRO_SHUTDOWN) {
165 handle_shutdown_ovf();
166 }
167 }
168
entropy_cc13xx_cc26xx_get_entropy_isr(const struct device * dev,uint8_t * buf,uint16_t len,uint32_t flags)169 static int entropy_cc13xx_cc26xx_get_entropy_isr(const struct device *dev,
170 uint8_t *buf, uint16_t len,
171 uint32_t flags)
172 {
173 struct entropy_cc13xx_cc26xx_data *data = dev->data;
174 uint16_t cnt;
175 uint16_t read = len;
176 uint32_t src;
177 uint32_t num[2];
178 unsigned int key;
179
180 key = irq_lock();
181 cnt = ring_buf_get(&data->pool, buf, len);
182 irq_unlock(key);
183
184 if ((cnt == len) || ((flags & ENTROPY_BUSYWAIT) == 0U)) {
185 read = cnt;
186 } else {
187 buf += cnt;
188 len -= cnt;
189
190 /* Allowed to busy-wait. We should use a polling approach */
191 while (len) {
192 key = irq_lock();
193
194 src = TRNGStatusGet();
195 if (src & TRNG_NUMBER_READY) {
196 /*
197 * This function acknowledges the ready
198 * status
199 */
200 num[1] = TRNGNumberGet(TRNG_HI_WORD);
201 num[0] = TRNGNumberGet(TRNG_LOW_WORD);
202
203 ring_buf_put(&data->pool, (uint8_t *)num,
204 sizeof(num));
205 }
206
207 /*
208 * If interrupts were enabled during busy wait, this
209 * would allow us to pick up anything that has been put
210 * in by the ISR as well.
211 */
212 cnt = ring_buf_get(&data->pool, buf, len);
213
214 if (src & TRNG_FRO_SHUTDOWN) {
215 handle_shutdown_ovf();
216 }
217
218 irq_unlock(key);
219
220 if (cnt) {
221 buf += cnt;
222 len -= cnt;
223 } else {
224 k_busy_wait(US_PER_SAMPLE);
225 }
226 }
227
228 }
229
230 return read;
231 }
232
233 #ifdef CONFIG_PM
234 /*
235 * ======== post_notify_fxn ========
236 * Called by Power module when waking up the CPU from Standby. The TRNG needs
237 * to be reconfigured afterwards, unless Zephyr's device PM turned it off, in
238 * which case it'd be responsible for turning it back on and reconfiguring it.
239 */
post_notify_fxn(unsigned int eventType,uintptr_t eventArg,uintptr_t clientArg)240 static int post_notify_fxn(unsigned int eventType, uintptr_t eventArg,
241 uintptr_t clientArg)
242 {
243 const struct device *dev = (const struct device *)clientArg;
244 int ret = Power_NOTIFYDONE;
245 int16_t res_id;
246
247 /* Reconfigure the hardware if returning from sleep */
248 if (eventType == PowerCC26XX_AWAKE_STANDBY) {
249 res_id = PowerCC26XX_PERIPH_TRNG;
250
251 if (Power_getDependencyCount(res_id) != 0) {
252 /* Reconfigure and enable TRNG only if powered */
253 start_trng(dev->data);
254 }
255 }
256
257 return (ret);
258 }
259 #endif
260
261 #ifdef CONFIG_PM_DEVICE
entropy_cc13xx_cc26xx_pm_action(const struct device * dev,enum pm_device_action action)262 static int entropy_cc13xx_cc26xx_pm_action(const struct device *dev,
263 enum pm_device_action action)
264 {
265 struct entropy_cc13xx_cc26xx_data *data = dev->data;
266
267 switch (action) {
268 case PM_DEVICE_ACTION_RESUME:
269 Power_setDependency(PowerCC26XX_PERIPH_TRNG);
270 start_trng(data);
271 break;
272 case PM_DEVICE_ACTION_SUSPEND:
273 stop_trng(data);
274 Power_releaseDependency(PowerCC26XX_PERIPH_TRNG);
275 break;
276 default:
277 return -ENOTSUP;
278 }
279
280 return 0;
281 }
282 #endif /* CONFIG_PM_DEVICE */
283
entropy_cc13xx_cc26xx_init(const struct device * dev)284 static int entropy_cc13xx_cc26xx_init(const struct device *dev)
285 {
286 struct entropy_cc13xx_cc26xx_data *data = dev->data;
287
288 /* Initialize driver data */
289 ring_buf_init(&data->pool, sizeof(data->data), data->data);
290
291 #if defined(CONFIG_PM)
292 Power_setDependency(PowerCC26XX_PERIPH_TRNG);
293 /* Stay out of standby until buffer is filled with entropy */
294 pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
295 data->constrained = true;
296 /* Register notification function */
297 Power_registerNotify(&data->post_notify,
298 PowerCC26XX_AWAKE_STANDBY,
299 post_notify_fxn, (uintptr_t)dev);
300 #else
301 /* Power TRNG domain */
302 PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
303
304 /* Enable TRNG peripheral clocks */
305 PRCMPeripheralRunEnable(PRCM_PERIPH_TRNG);
306 /* Enabled the TRNG while in sleep mode to keep the entropy pool full. After
307 * the pool is full the TRNG will enter idle mode when random numbers are no
308 * longer being read. */
309 PRCMPeripheralSleepEnable(PRCM_PERIPH_TRNG);
310 PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_TRNG);
311
312
313 /* Load PRCM settings */
314 PRCMLoadSet();
315 while (!PRCMLoadGet()) {
316 continue;
317 }
318
319 /* Peripherals should not be accessed until power domain is on. */
320 while (PRCMPowerDomainsAllOn(PRCM_DOMAIN_PERIPH) !=
321 PRCM_DOMAIN_POWER_ON) {
322 continue;
323 }
324 #endif
325
326 start_trng(data);
327
328 IRQ_CONNECT(DT_INST_IRQN(0),
329 DT_INST_IRQ(0, priority),
330 entropy_cc13xx_cc26xx_isr,
331 DEVICE_DT_INST_GET(0), 0);
332 irq_enable(DT_INST_IRQN(0));
333
334 return 0;
335 }
336
337 static const struct entropy_driver_api entropy_cc13xx_cc26xx_driver_api = {
338 .get_entropy = entropy_cc13xx_cc26xx_get_entropy,
339 .get_entropy_isr = entropy_cc13xx_cc26xx_get_entropy_isr,
340 };
341
342 static struct entropy_cc13xx_cc26xx_data entropy_cc13xx_cc26xx_data = {
343 .lock = Z_SEM_INITIALIZER(entropy_cc13xx_cc26xx_data.lock, 1, 1),
344 .sync = Z_SEM_INITIALIZER(entropy_cc13xx_cc26xx_data.sync, 0, 1),
345 };
346
347 PM_DEVICE_DT_INST_DEFINE(0, entropy_cc13xx_cc26xx_pm_action);
348
349 DEVICE_DT_INST_DEFINE(0,
350 entropy_cc13xx_cc26xx_init,
351 PM_DEVICE_DT_INST_GET(0),
352 &entropy_cc13xx_cc26xx_data, NULL,
353 PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
354 &entropy_cc13xx_cc26xx_driver_api);
355