drivers: stm32: random: Initial STM32 random number generator driver

Add a STM32 LL based driver for the RNG processor. The RNG processor
is a random number generator, based on a continuous analog noise, that
provides a random 32-bit value to the host when read. The RNG passed
the FIPS PUB 140-2 (2001 October 10) tests with a success ratio of 99%.

Signed-off-by: Erwin Rol <erwin@erwinrol.com>
diff --git a/arch/arm/soc/st_stm32/stm32f4/soc.h b/arch/arm/soc/st_stm32/stm32f4/soc.h
index 032610d..1c99fda 100644
--- a/arch/arm/soc/st_stm32/stm32f4/soc.h
+++ b/arch/arm/soc/st_stm32/stm32f4/soc.h
@@ -64,6 +64,10 @@
 #include <stm32f4xx_ll_i2c.h>
 #endif
 
+#ifdef CONFIG_RANDOM_STM32_RNG
+#include <stm32f4xx_ll_rng.h>
+#endif
+
 #endif /* !_ASMLANGUAGE */
 
 #endif /* _STM32F4_SOC_H_ */
diff --git a/drivers/random/Kconfig b/drivers/random/Kconfig
index 4efccd7..5bbd7b9 100644
--- a/drivers/random/Kconfig
+++ b/drivers/random/Kconfig
@@ -16,6 +16,7 @@
 if RANDOM_GENERATOR
 
 source "drivers/random/Kconfig.mcux"
+source "drivers/random/Kconfig.stm32"
 
 config RANDOM_HAS_DRIVER
 	bool
diff --git a/drivers/random/Kconfig.stm32 b/drivers/random/Kconfig.stm32
new file mode 100644
index 0000000..72d0c61
--- /dev/null
+++ b/drivers/random/Kconfig.stm32
@@ -0,0 +1,15 @@
+# Kconfig.stm32 - STM32 random generator driver configuration
+#
+# Copyright (c) 2017 Erwin Rol <erwin@erwinrol.com>
+#
+# SPDX-License-Identifier: Apache-2.0
+
+menuconfig RANDOM_STM32_RNG
+	bool "STM32 RNG driver"
+	depends on RANDOM_GENERATOR
+	default n
+	select RANDOM_HAS_DRIVER
+	help
+	  This option enables the RNG processor, which is a random number
+	  generator, based on a continuous analog noise, that provides
+	  a random 32-bit value to the host when read.
diff --git a/drivers/random/Makefile b/drivers/random/Makefile
index db1f652..5eb7c6e 100644
--- a/drivers/random/Makefile
+++ b/drivers/random/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_RANDOM_MCUX_TRNG) += random_mcux_trng.o
 obj-$(CONFIG_TIMER_RANDOM_GENERATOR) = rand32_timer.o
 obj-$(CONFIG_X86_TSC_RANDOM_GENERATOR) += rand32_timestamp.o
+obj-$(CONFIG_RANDOM_STM32_RNG) += random_stm32_rng.o
diff --git a/drivers/random/random_stm32_rng.c b/drivers/random/random_stm32_rng.c
new file mode 100644
index 0000000..8b3810a
--- /dev/null
+++ b/drivers/random/random_stm32_rng.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2017 Erwin Rol <erwin@erwinrol.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <kernel.h>
+#include <device.h>
+#include <random.h>
+#include <drivers/rand32.h>
+#include <init.h>
+#include <misc/__assert.h>
+#include <misc/util.h>
+#include <errno.h>
+#include <soc.h>
+#include <misc/printk.h>
+#include <clock_control.h>
+#include <clock_control/stm32_clock_control.h>
+
+struct random_stm32_rng_dev_cfg {
+	struct stm32_pclken pclken;
+};
+
+struct random_stm32_rng_dev_data {
+	RNG_TypeDef *rng;
+	struct device *clock;
+};
+
+#define DEV_DATA(dev) \
+	((struct random_stm32_rng_dev_data *)(dev)->driver_data)
+
+#define DEV_CFG(dev) \
+	((struct random_stm32_rng_dev_cfg *)(dev)->config->config_info)
+
+static void random_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 random_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 random_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 (random_stm32_got_error(rng)) {
+			return -EIO;
+		}
+
+		if (timeout-- == 0) {
+			return -ETIMEDOUT;
+		}
+
+		k_yield();
+	}
+
+	if (random_stm32_got_error(rng)) {
+		return -EIO;
+	} else {
+		return 0;
+	}
+}
+
+static int random_stm32_rng_get_entropy(struct device *dev, u8_t *buffer,
+					u16_t length)
+{
+	struct random_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 (random_stm32_got_error(dev_data->rng)) {
+		random_stm32_rng_reset(dev_data->rng);
+	}
+
+	while (length > 0) {
+		u32_t rndbits;
+		u8_t *p_rndbits = (u8_t *)&rndbits;
+
+		res = random_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 random_stm32_rng_init(struct device *dev)
+{
+	struct random_stm32_rng_dev_data *dev_data;
+	struct random_stm32_rng_dev_cfg *dev_cfg;
+
+	__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);
+
+	dev_data->clock = device_get_binding(STM32_CLOCK_CONTROL_NAME);
+	__ASSERT_NO_MSG(dev_data->clock != NULL);
+
+	clock_control_on(dev_data->clock,
+		(clock_control_subsys_t *)&dev_cfg->pclken);
+
+	LL_RNG_Enable(dev_data->rng);
+
+	return 0;
+}
+
+static const struct random_driver_api random_stm32_rng_api = {
+	.get_entropy = random_stm32_rng_get_entropy
+};
+
+static const struct random_stm32_rng_dev_cfg random_stm32_rng_config = {
+	.pclken	= { .bus = STM32_CLOCK_BUS_AHB2,
+		    .enr = LL_AHB2_GRP1_PERIPH_RNG },
+};
+
+static struct random_stm32_rng_dev_data random_stm32_rng_data = {
+	.rng = RNG,
+};
+
+DEVICE_AND_API_INIT(random_stm32_rng, CONFIG_RANDOM_NAME,
+		    random_stm32_rng_init,
+		    &random_stm32_rng_data, &random_stm32_rng_config,
+		    PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
+		    &random_stm32_rng_api);
+
+u32_t sys_rand32_get(void)
+{
+	u32_t output;
+	int rc;
+
+	rc = random_stm32_rng_get_entropy(DEVICE_GET(random_stm32_rng),
+					  (u8_t *) &output, sizeof(output));
+	__ASSERT_NO_MSG(!rc);
+
+	return output;
+}
+
diff --git a/ext/hal/st/stm32cube/Kbuild b/ext/hal/st/stm32cube/Kbuild
index db6b1ab..a87cb0a 100644
--- a/ext/hal/st/stm32cube/Kbuild
+++ b/ext/hal/st/stm32cube/Kbuild
@@ -27,6 +27,7 @@
 obj-$(CONFIG_SERIAL_HAS_DRIVER) += stm32f4xx/drivers/src/stm32f4xx_hal_uart.o
 obj-$(CONFIG_I2C) += stm32f4xx/drivers/src/stm32f4xx_ll_i2c.o
 obj-$(CONFIG_SPI_STM32) += stm32f4xx/drivers/src/stm32f4xx_ll_spi.o
+obj-$(CONFIG_RANDOM_STM32_RNG) += stm32f4xx/drivers/src/stm32f4xx_ll_rng.o
 obj-y += stm32f4xx/soc/system_stm32f4xx.o
 endif