mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 09:34:18 +00:00
feat(st-crypto): add STM32 RNG driver
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>
This commit is contained in:
parent
4bb4e83649
commit
af8dee20d5
2 changed files with 284 additions and 0 deletions
269
drivers/st/crypto/stm32_rng.c
Normal file
269
drivers/st/crypto/stm32_rng.c
Normal file
|
@ -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();
|
||||
}
|
15
include/drivers/st/stm32_rng.h
Normal file
15
include/drivers/st/stm32_rng.h
Normal file
|
@ -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 */
|
Loading…
Add table
Reference in a new issue