1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT espressif_esp32_trng
8 
9 #include <string.h>
10 #include <soc/rtc.h>
11 #include <soc/wdev_reg.h>
12 #include <esp_system.h>
13 #include <soc.h>
14 #include <esp_cpu.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/drivers/entropy.h>
17 #if defined(SOC_RNG_CLOCK_IS_INDEPENDENT)
18 #include <zephyr/drivers/clock_control.h>
19 #endif
20 
21 LOG_MODULE_REGISTER(entropy, CONFIG_ENTROPY_LOG_LEVEL);
22 
entropy_esp32_get_u32(void)23 static inline uint32_t entropy_esp32_get_u32(void)
24 {
25 	/* The PRNG which implements WDEV_RANDOM register gets 2 bits
26 	 * of extra entropy from a hardware randomness source every APB clock cycle
27 	 * (provided WiFi or BT are enabled). To make sure entropy is not drained
28 	 * faster than it is added, this function needs to wait for at least 16 APB
29 	 * clock cycles after reading previous word. This implementation may actually
30 	 * wait a bit longer due to extra time spent in arithmetic and branch statements.
31 	 */
32 
33 	uint32_t cpu_to_apb_freq_ratio =
34 		esp_clk_cpu_freq() / esp_clk_apb_freq();
35 
36 	static uint32_t last_ccount;
37 	uint32_t ccount;
38 
39 	do {
40 		ccount = esp_cpu_get_cycle_count();
41 	} while (ccount - last_ccount < cpu_to_apb_freq_ratio * 16);
42 	last_ccount = ccount;
43 
44 	return REG_READ(WDEV_RND_REG);
45 }
46 
entropy_esp32_get_entropy(const struct device * dev,uint8_t * buf,uint16_t len)47 static int entropy_esp32_get_entropy(const struct device *dev, uint8_t *buf,
48 				     uint16_t len)
49 {
50 	assert(buf != NULL);
51 	uint8_t *buf_bytes = buf;
52 
53 	while (len > 0) {
54 		uint32_t word = entropy_esp32_get_u32();
55 		uint32_t to_copy = MIN(sizeof(word), len);
56 
57 		memcpy(buf_bytes, &word, to_copy);
58 		buf_bytes += to_copy;
59 		len -= to_copy;
60 	}
61 
62 	return 0;
63 }
64 
entropy_esp32_init(const struct device * dev)65 static int entropy_esp32_init(const struct device *dev)
66 {
67 	int ret = 0;
68 
69 #if defined(SOC_RNG_CLOCK_IS_INDEPENDENT)
70 	const struct device *clock_dev =
71 		DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(trng0)));
72 	clock_control_subsys_t clock_subsys =
73 		(clock_control_subsys_t)DT_CLOCKS_CELL(DT_NODELABEL(trng0), offset);
74 
75 	if (!device_is_ready(clock_dev)) {
76 		return -ENODEV;
77 	}
78 
79 	ret = clock_control_on(clock_dev, clock_subsys);
80 
81 	if (ret != 0) {
82 		LOG_ERR("Error enabling TRNG clock");
83 	}
84 #else
85 	/* clock initialization handled by clock manager */
86 #endif
87 
88 	return ret;
89 }
90 
91 static DEVICE_API(entropy, entropy_esp32_api_funcs) = {
92 	.get_entropy = entropy_esp32_get_entropy
93 };
94 
95 DEVICE_DT_INST_DEFINE(0,
96 		    entropy_esp32_init, NULL, NULL, NULL,
97 		    PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
98 		    &entropy_esp32_api_funcs);
99