123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- /*
- * 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) {
- if ((mmio_read_32(stm32_rng.base + RNG_SR) & RNG_SR_DRDY) == 0U) {
- break;
- }
- 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();
- }
|