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