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
23 #if SOC_LP_TIMER_SUPPORTED
24 #include "hal/lp_timer_hal.h"
25 #endif
26
27 #if defined CONFIG_SOC_SERIES_ESP32S3
28 /* If APB clock is 80 MHz, the maximum sampling frequency is around 45 KHz */
29 /* 45 KHz reading frequency is the maximum we have tested so far on S3 */
30 #define APB_CYCLE_WAIT_NUM (1778)
31 #elif defined CONFIG_SOC_SERIES_ESP32C6
32 /* On ESP32C6, we only read one byte at a time, then XOR the value with
33 * an asynchronous timer (see code below).
34 * The current value translates to a sampling frequency of around 62.5 KHz
35 * for reading 8 bit samples, which is the rate at which the RNG was tested,
36 * plus additional overhead for the calculation, making it slower.
37 */
38 #define APB_CYCLE_WAIT_NUM (160 * 16)
39 #else
40 #define APB_CYCLE_WAIT_NUM (16)
41 #endif
42
entropy_esp32_get_u32(void)43 static inline uint32_t entropy_esp32_get_u32(void)
44 {
45 /* The PRNG which implements WDEV_RANDOM register gets 2 bits
46 * of extra entropy from a hardware randomness source every APB clock cycle
47 * (provided WiFi or BT are enabled). To make sure entropy is not drained
48 * faster than it is added, this function needs to wait for at least 16 APB
49 * clock cycles after reading previous word. This implementation may actually
50 * wait a bit longer due to extra time spent in arithmetic and branch statements.
51 */
52
53 uint32_t cpu_to_apb_freq_ratio = esp_clk_cpu_freq() / esp_clk_apb_freq();
54
55 static uint32_t last_ccount;
56 uint32_t ccount;
57 uint32_t result = 0;
58 #if SOC_LP_TIMER_SUPPORTED
59 for (size_t i = 0; i < sizeof(result); i++) {
60 do {
61 ccount = esp_cpu_get_cycle_count();
62 result ^= REG_READ(WDEV_RND_REG);
63 } while (ccount - last_ccount < cpu_to_apb_freq_ratio * APB_CYCLE_WAIT_NUM);
64 uint32_t current_rtc_timer_counter = (lp_timer_hal_get_cycle_count() & 0xFF);
65
66 result ^= ((result ^ current_rtc_timer_counter) & 0xFF) << (i * 8);
67 }
68 #else
69 do {
70 ccount = esp_cpu_get_cycle_count();
71 result ^= REG_READ(WDEV_RND_REG);
72 } while (ccount - last_ccount < cpu_to_apb_freq_ratio * APB_CYCLE_WAIT_NUM);
73 #endif
74 last_ccount = ccount;
75 return result ^ REG_READ(WDEV_RND_REG);
76 }
77
entropy_esp32_get_entropy(const struct device * dev,uint8_t * buf,uint16_t len)78 static int entropy_esp32_get_entropy(const struct device *dev, uint8_t *buf,
79 uint16_t len)
80 {
81 assert(buf != NULL);
82 uint8_t *buf_bytes = buf;
83
84 while (len > 0) {
85 uint32_t word = entropy_esp32_get_u32();
86 uint32_t to_copy = MIN(sizeof(word), len);
87
88 memcpy(buf_bytes, &word, to_copy);
89 buf_bytes += to_copy;
90 len -= to_copy;
91 }
92
93 return 0;
94 }
95
entropy_esp32_init(const struct device * dev)96 static int entropy_esp32_init(const struct device *dev)
97 {
98 int ret = 0;
99
100 #if defined(SOC_RNG_CLOCK_IS_INDEPENDENT)
101 const struct device *clock_dev =
102 DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(trng0)));
103 clock_control_subsys_t clock_subsys =
104 (clock_control_subsys_t)DT_CLOCKS_CELL(DT_NODELABEL(trng0), offset);
105
106 if (!device_is_ready(clock_dev)) {
107 return -ENODEV;
108 }
109
110 ret = clock_control_on(clock_dev, clock_subsys);
111
112 if (ret != 0) {
113 LOG_ERR("Error enabling TRNG clock");
114 }
115 #else
116 /* clock initialization handled by clock manager */
117 #endif
118
119 return ret;
120 }
121
122 static DEVICE_API(entropy, entropy_esp32_api_funcs) = {
123 .get_entropy = entropy_esp32_get_entropy
124 };
125
126 DEVICE_DT_INST_DEFINE(0,
127 entropy_esp32_init, NULL, NULL, NULL,
128 PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
129 &entropy_esp32_api_funcs);
130