Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | /*
* Copyright (c) 2017 Erwin Rol <erwin@erwinrol.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <device.h>
#include <drivers/entropy.h>
#include <random/rand32.h>
#include <init.h>
#include <sys/__assert.h>
#include <sys/util.h>
#include <errno.h>
#include <soc.h>
#include <sys/printk.h>
#include <drivers/clock_control.h>
#include <clock_control/stm32_clock_control.h>
#if !defined(CONFIG_SOC_SERIES_STM32L4X) && !defined(CONFIG_SOC_SERIES_STM32F4X) && !defined(CONFIG_SOC_SERIES_STM32F7X)
#error RNG only available on STM32F4, STM32F7 and STM32L4 series
#elif defined(CONFIG_SOC_STM32F401XE)
#error RNG not available on STM32F401 based SoCs
#elif defined(CONFIG_SOC_STM32F411XE)
#error RNG not available on STM32F411 based SoCs
#else
struct entropy_stm32_rng_dev_cfg {
struct stm32_pclken pclken;
};
struct entropy_stm32_rng_dev_data {
RNG_TypeDef *rng;
struct device *clock;
};
#define DEV_DATA(dev) \
((struct entropy_stm32_rng_dev_data *)(dev)->driver_data)
#define DEV_CFG(dev) \
((struct entropy_stm32_rng_dev_cfg *)(dev)->config->config_info)
static void entropy_stm32_rng_reset(RNG_TypeDef *rng)
{
__ASSERT_NO_MSG(rng != NULL);
/* Reset RNG as described in RM0090 Reference manual
* section 24.3.2 Error management.
*/
LL_RNG_ClearFlag_CEIS(rng);
LL_RNG_ClearFlag_SEIS(rng);
LL_RNG_Disable(rng);
LL_RNG_Enable(rng);
}
static int entropy_stm32_got_error(RNG_TypeDef *rng)
{
__ASSERT_NO_MSG(rng != NULL);
if (LL_RNG_IsActiveFlag_CECS(rng)) {
return 1;
}
if (LL_RNG_IsActiveFlag_SECS(rng)) {
return 1;
}
return 0;
}
static int entropy_stm32_wait_ready(RNG_TypeDef *rng)
{
/* Agording to the reference manual it takes 40 periods
* of the RNG_CLK clock signal between two consecutive
* random numbers. Also RNG_CLK may not be smaller than
* HCLK/16. So it should not take more than 640 HCLK
* ticks. Assuming the CPU can do 1 instruction per HCLK
* the number of times to loop before the RNG is ready
* is less than 1000. And that is when assumming the loop
* only takes 1 instruction. So looping a million times
* should be more than enough.
*/
int timeout = 1000000;
__ASSERT_NO_MSG(rng != NULL);
while (!LL_RNG_IsActiveFlag_DRDY(rng)) {
if (entropy_stm32_got_error(rng)) {
return -EIO;
}
if (timeout-- == 0) {
return -ETIMEDOUT;
}
k_yield();
}
if (entropy_stm32_got_error(rng)) {
return -EIO;
} else {
return 0;
}
}
static int entropy_stm32_rng_get_entropy(struct device *dev, u8_t *buffer,
u16_t length)
{
struct entropy_stm32_rng_dev_data *dev_data;
int n = sizeof(u32_t);
int res;
__ASSERT_NO_MSG(dev != NULL);
__ASSERT_NO_MSG(buffer != NULL);
dev_data = DEV_DATA(dev);
__ASSERT_NO_MSG(dev_data != NULL);
/* if the RNG has errors reset it before use */
if (entropy_stm32_got_error(dev_data->rng)) {
entropy_stm32_rng_reset(dev_data->rng);
}
while (length > 0) {
u32_t rndbits;
u8_t *p_rndbits = (u8_t *)&rndbits;
res = entropy_stm32_wait_ready(dev_data->rng);
if (res < 0)
return res;
rndbits = LL_RNG_ReadRandData32(dev_data->rng);
if (length < sizeof(u32_t))
n = length;
for (int i = 0; i < n; i++) {
*buffer++ = *p_rndbits++;
}
length -= n;
}
return 0;
}
static int entropy_stm32_rng_init(struct device *dev)
{
struct entropy_stm32_rng_dev_data *dev_data;
struct entropy_stm32_rng_dev_cfg *dev_cfg;
int res;
__ASSERT_NO_MSG(dev != NULL);
dev_data = DEV_DATA(dev);
dev_cfg = DEV_CFG(dev);
__ASSERT_NO_MSG(dev_data != NULL);
__ASSERT_NO_MSG(dev_cfg != NULL);
#if CONFIG_SOC_SERIES_STM32L4X
/* Configure PLLSA11 to enable 48M domain */
LL_RCC_PLLSAI1_ConfigDomain_48M(LL_RCC_PLLSOURCE_MSI,
LL_RCC_PLLM_DIV_1,
24, LL_RCC_PLLSAI1Q_DIV_2);
/* Enable PLLSA1 */
LL_RCC_PLLSAI1_Enable();
/* Enable PLLSAI1 output mapped on 48MHz domain clock */
LL_RCC_PLLSAI1_EnableDomain_48M();
/* Wait for PLLSA1 ready flag */
while (LL_RCC_PLLSAI1_IsReady() != 1) {
}
/* Write the peripherals independent clock configuration register :
* choose PLLSAI1 source as the 48 MHz clock is needed for the RNG
* Linear Feedback Shift Register
*/
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_PLLSAI1);
#endif /* CONFIG_SOC_SERIES_STM32L4X */
dev_data->clock = device_get_binding(STM32_CLOCK_CONTROL_NAME);
__ASSERT_NO_MSG(dev_data->clock != NULL);
res = clock_control_on(dev_data->clock,
(clock_control_subsys_t *)&dev_cfg->pclken);
__ASSERT_NO_MSG(res == 0);
LL_RNG_Enable(dev_data->rng);
return 0;
}
static const struct entropy_driver_api entropy_stm32_rng_api = {
.get_entropy = entropy_stm32_rng_get_entropy
};
static const struct entropy_stm32_rng_dev_cfg entropy_stm32_rng_config = {
.pclken = { .bus = STM32_CLOCK_BUS_AHB2,
.enr = LL_AHB2_GRP1_PERIPH_RNG },
};
static struct entropy_stm32_rng_dev_data entropy_stm32_rng_data = {
.rng = RNG,
};
DEVICE_AND_API_INIT(entropy_stm32_rng, CONFIG_ENTROPY_NAME,
entropy_stm32_rng_init,
&entropy_stm32_rng_data, &entropy_stm32_rng_config,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&entropy_stm32_rng_api);
#endif
|