99 lines
2.4 KiB
C
99 lines
2.4 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT espressif_esp32_trng
|
|
|
|
#include <string.h>
|
|
#include <soc/rtc.h>
|
|
#include <soc/wdev_reg.h>
|
|
#include <esp_system.h>
|
|
#include <soc.h>
|
|
#include <esp_cpu.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/drivers/entropy.h>
|
|
#if defined(SOC_RNG_CLOCK_IS_INDEPENDENT)
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#endif
|
|
|
|
LOG_MODULE_REGISTER(entropy, CONFIG_ENTROPY_LOG_LEVEL);
|
|
|
|
static inline uint32_t entropy_esp32_get_u32(void)
|
|
{
|
|
/* The PRNG which implements WDEV_RANDOM register gets 2 bits
|
|
* of extra entropy from a hardware randomness source every APB clock cycle
|
|
* (provided WiFi or BT are enabled). To make sure entropy is not drained
|
|
* faster than it is added, this function needs to wait for at least 16 APB
|
|
* clock cycles after reading previous word. This implementation may actually
|
|
* wait a bit longer due to extra time spent in arithmetic and branch statements.
|
|
*/
|
|
|
|
uint32_t cpu_to_apb_freq_ratio =
|
|
esp_clk_cpu_freq() / esp_clk_apb_freq();
|
|
|
|
static uint32_t last_ccount;
|
|
uint32_t ccount;
|
|
|
|
do {
|
|
ccount = esp_cpu_get_cycle_count();
|
|
} while (ccount - last_ccount < cpu_to_apb_freq_ratio * 16);
|
|
last_ccount = ccount;
|
|
|
|
return REG_READ(WDEV_RND_REG);
|
|
}
|
|
|
|
static int entropy_esp32_get_entropy(const struct device *dev, uint8_t *buf,
|
|
uint16_t len)
|
|
{
|
|
assert(buf != NULL);
|
|
uint8_t *buf_bytes = buf;
|
|
|
|
while (len > 0) {
|
|
uint32_t word = entropy_esp32_get_u32();
|
|
uint32_t to_copy = MIN(sizeof(word), len);
|
|
|
|
memcpy(buf_bytes, &word, to_copy);
|
|
buf_bytes += to_copy;
|
|
len -= to_copy;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int entropy_esp32_init(const struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
#if defined(SOC_RNG_CLOCK_IS_INDEPENDENT)
|
|
const struct device *clock_dev =
|
|
DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_NODELABEL(trng0)));
|
|
clock_control_subsys_t clock_subsys =
|
|
(clock_control_subsys_t)DT_CLOCKS_CELL(DT_NODELABEL(trng0), offset);
|
|
|
|
if (!device_is_ready(clock_dev)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = clock_control_on(clock_dev, clock_subsys);
|
|
|
|
if (ret != 0) {
|
|
LOG_ERR("Error enabling TRNG clock");
|
|
}
|
|
#else
|
|
/* clock initialization handled by clock manager */
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct entropy_driver_api entropy_esp32_api_funcs = {
|
|
.get_entropy = entropy_esp32_get_entropy
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0,
|
|
entropy_esp32_init, NULL, NULL, NULL,
|
|
PRE_KERNEL_1, CONFIG_ENTROPY_INIT_PRIORITY,
|
|
&entropy_esp32_api_funcs);
|