]> git.baikalelectronics.ru Git - arm-tf.git/commitdiff
feat(st-crypto): add STM32 RNG driver
authorYann Gautier <yann.gautier@st.com>
Thu, 18 Apr 2019 12:47:35 +0000 (14:47 +0200)
committerLionel Debieve <lionel.debieve@foss.st.com>
Mon, 14 Nov 2022 09:55:17 +0000 (10:55 +0100)
This driver manages the STM32 Random Number Generator
peripheral.

Change-Id: I4403ebb2dbdaa8df993a4413f1ef48eeba00427c
Signed-off-by: Yann Gautier <yann.gautier@st.com>
Signed-off-by: Lionel Debieve <lionel.debieve@foss.st.com>
drivers/st/crypto/stm32_rng.c [new file with mode: 0644]
include/drivers/st/stm32_rng.h [new file with mode: 0644]

diff --git a/drivers/st/crypto/stm32_rng.c b/drivers/st/crypto/stm32_rng.c
new file mode 100644 (file)
index 0000000..a9dc43f
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <arch_helpers.h>
+#include <drivers/clk.h>
+#include <drivers/delay_timer.h>
+#include <drivers/st/stm32_rng.h>
+#include <drivers/st/stm32mp_reset.h>
+#include <lib/mmio.h>
+#include <libfdt.h>
+
+#include <platform_def.h>
+
+#if STM32_RNG_VER == 2
+#define DT_RNG_COMPAT          "st,stm32-rng"
+#endif
+#if STM32_RNG_VER == 4
+#define DT_RNG_COMPAT          "st,stm32mp13-rng"
+#endif
+#define RNG_CR                 0x00U
+#define RNG_SR                 0x04U
+#define RNG_DR                 0x08U
+
+#define RNG_CR_RNGEN           BIT(2)
+#define RNG_CR_IE              BIT(3)
+#define RNG_CR_CED             BIT(5)
+#define RNG_CR_CLKDIV          GENMASK(19, 16)
+#define RNG_CR_CLKDIV_SHIFT    16U
+#define RNG_CR_CONDRST         BIT(30)
+
+#define RNG_SR_DRDY            BIT(0)
+#define RNG_SR_CECS            BIT(1)
+#define RNG_SR_SECS            BIT(2)
+#define RNG_SR_CEIS            BIT(5)
+#define RNG_SR_SEIS            BIT(6)
+
+#define RNG_TIMEOUT_US         100000U
+#define RNG_TIMEOUT_STEP_US    10U
+
+#define TIMEOUT_US_1MS         1000U
+
+#define RNG_NIST_CONFIG_A      0x00F40F00U
+#define RNG_NIST_CONFIG_B      0x01801000U
+#define RNG_NIST_CONFIG_C      0x00F00D00U
+#define RNG_NIST_CONFIG_MASK   GENMASK(25, 8)
+
+#define RNG_MAX_NOISE_CLK_FREQ 48000000U
+
+struct stm32_rng_instance {
+       uintptr_t base;
+       unsigned long clock;
+};
+
+static struct stm32_rng_instance stm32_rng;
+
+static void seed_error_recovery(void)
+{
+       uint8_t i __maybe_unused;
+
+       /* Recommended by the SoC reference manual */
+       mmio_clrbits_32(stm32_rng.base + RNG_SR, RNG_SR_SEIS);
+       dmbsy();
+
+#if STM32_RNG_VER == 2
+       /* No Auto-reset on version 2, need to clean FIFO */
+       for (i = 12U; i != 0U; i--) {
+               (void)mmio_read_32(stm32_rng.base + RNG_DR);
+       }
+
+       dmbsy();
+#endif
+
+       if ((mmio_read_32(stm32_rng.base + RNG_SR) & RNG_SR_SEIS) != 0U) {
+               ERROR("RNG noise\n");
+               panic();
+       }
+}
+
+static uint32_t stm32_rng_clock_freq_restrain(void)
+{
+       unsigned long clock_rate;
+       uint32_t clock_div = 0U;
+
+       clock_rate = clk_get_rate(stm32_rng.clock);
+
+       /*
+        * Get the exponent to apply on the CLKDIV field in RNG_CR register
+        * No need to handle the case when clock-div > 0xF as it is physically
+        * impossible
+        */
+       while ((clock_rate >> clock_div) > RNG_MAX_NOISE_CLK_FREQ) {
+               clock_div++;
+       }
+
+       VERBOSE("RNG clk rate : %lu\n", clk_get_rate(stm32_rng.clock) >> clock_div);
+
+       return clock_div;
+}
+
+static int stm32_rng_enable(void)
+{
+       uint32_t sr;
+       uint64_t timeout;
+       uint32_t clock_div __maybe_unused;
+
+#if STM32_RNG_VER == 2
+       mmio_write_32(stm32_rng.base + RNG_CR, RNG_CR_RNGEN | RNG_CR_CED);
+#endif
+#if STM32_RNG_VER == 4
+       /* Reset internal block and disable CED bit */
+       clock_div = stm32_rng_clock_freq_restrain();
+
+       /* Update configuration fields */
+       mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_NIST_CONFIG_MASK,
+                          RNG_NIST_CONFIG_A | RNG_CR_CONDRST | RNG_CR_CED);
+
+       mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_CR_CLKDIV,
+                          (clock_div << RNG_CR_CLKDIV_SHIFT));
+
+       mmio_clrsetbits_32(stm32_rng.base + RNG_CR, RNG_CR_CONDRST, RNG_CR_RNGEN);
+#endif
+       timeout = timeout_init_us(RNG_TIMEOUT_US);
+       sr = mmio_read_32(stm32_rng.base + RNG_SR);
+       while ((sr & RNG_SR_DRDY) == 0U) {
+               if (timeout_elapsed(timeout)) {
+                       WARN("Timeout waiting\n");
+                       return -ETIMEDOUT;
+               }
+
+               if ((sr & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) {
+                       seed_error_recovery();
+                       timeout = timeout_init_us(RNG_TIMEOUT_US);
+               }
+
+               udelay(RNG_TIMEOUT_STEP_US);
+               sr = mmio_read_32(stm32_rng.base + RNG_SR);
+       }
+
+       VERBOSE("Init RNG done\n");
+
+       return 0;
+}
+
+/*
+ * stm32_rng_read - Read a number of random bytes from RNG
+ * out: pointer to the output buffer
+ * size: number of bytes to be read
+ * Return 0 on success, non-0 on failure
+ */
+int stm32_rng_read(uint8_t *out, uint32_t size)
+{
+       uint8_t *buf = out;
+       size_t len = size;
+       int nb_tries;
+       uint32_t data32;
+       int rc = 0;
+       unsigned int count;
+
+       if (stm32_rng.base == 0U) {
+               return -EPERM;
+       }
+
+       while (len != 0U) {
+               nb_tries = RNG_TIMEOUT_US / RNG_TIMEOUT_STEP_US;
+               do {
+                       uint32_t status = mmio_read_32(stm32_rng.base + RNG_SR);
+
+                       if ((status & (RNG_SR_SECS | RNG_SR_SEIS)) != 0U) {
+                               seed_error_recovery();
+                       }
+
+                       udelay(RNG_TIMEOUT_STEP_US);
+                       nb_tries--;
+                       if (nb_tries == 0) {
+                               rc = -ETIMEDOUT;
+                               goto bail;
+                       }
+               } while ((mmio_read_32(stm32_rng.base + RNG_SR) &
+                         RNG_SR_DRDY) == 0U);
+
+               count = 4U;
+               while (len != 0U) {
+                       data32 = mmio_read_32(stm32_rng.base + RNG_DR);
+                       count--;
+
+                       memcpy(buf, &data32, MIN(len, sizeof(uint32_t)));
+                       buf += MIN(len, sizeof(uint32_t));
+                       len -= MIN(len, sizeof(uint32_t));
+
+                       if (count == 0U) {
+                               break;
+                       }
+               }
+       }
+
+bail:
+       if (rc != 0) {
+               memset(out, 0, buf - out);
+       }
+
+       return rc;
+}
+
+/*
+ * stm32_rng_init: Initialize rng from DT
+ * return 0 on success, negative value on failure
+ */
+int stm32_rng_init(void)
+{
+       void *fdt;
+       struct dt_node_info dt_rng;
+       int node;
+
+       if (stm32_rng.base != 0U) {
+               /* Driver is already initialized */
+               return 0;
+       }
+
+       if (fdt_get_address(&fdt) == 0) {
+               panic();
+       }
+
+       node = dt_get_node(&dt_rng, -1, DT_RNG_COMPAT);
+       if (node < 0) {
+               return 0;
+       }
+
+       if (dt_rng.status == DT_DISABLED) {
+               return 0;
+       }
+
+       assert(dt_rng.base != 0U);
+
+       stm32_rng.base = dt_rng.base;
+
+       if (dt_rng.clock < 0) {
+               panic();
+       }
+
+       stm32_rng.clock = (unsigned long)dt_rng.clock;
+       clk_enable(stm32_rng.clock);
+
+       if (dt_rng.reset >= 0) {
+               int ret;
+
+               ret = stm32mp_reset_assert((unsigned long)dt_rng.reset,
+                                          TIMEOUT_US_1MS);
+               if (ret != 0) {
+                       panic();
+               }
+
+               udelay(20);
+
+               ret = stm32mp_reset_deassert((unsigned long)dt_rng.reset,
+                                            TIMEOUT_US_1MS);
+               if (ret != 0) {
+                       panic();
+               }
+       }
+
+       return stm32_rng_enable();
+}
diff --git a/include/drivers/st/stm32_rng.h b/include/drivers/st/stm32_rng.h
new file mode 100644 (file)
index 0000000..6ac064d
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32_RNG_H
+#define STM32_RNG_H
+
+#include <stdint.h>
+
+int stm32_rng_read(uint8_t *out, uint32_t size);
+int stm32_rng_init(void);
+
+#endif /* STM32_RNG_H */