From 77e0c6087923574579abe1a04538bb4982e33d55 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Tue, 19 Sep 2023 17:27:53 +0200 Subject: rng: stm32: rename STM32 RNG driver Rename the RNG driver as it is usable by other STM32 platforms than the STM32MP1x ones. Rename CONFIG_RNG_STM32MP1 to CONFIG_RNG_STM32 Signed-off-by: Gatien Chevallier Reviewed-by: Grzegorz Szymaszek Reviewed-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/rng/Kconfig | 8 +- drivers/rng/Makefile | 2 +- drivers/rng/stm32_rng.c | 198 +++++++++++++++++++++++++++++++++++++++++++++ drivers/rng/stm32mp1_rng.c | 198 --------------------------------------------- 4 files changed, 203 insertions(+), 203 deletions(-) create mode 100644 drivers/rng/stm32_rng.c delete mode 100644 drivers/rng/stm32mp1_rng.c (limited to 'drivers') diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig index 5deb5db5b71..24666bff987 100644 --- a/drivers/rng/Kconfig +++ b/drivers/rng/Kconfig @@ -48,11 +48,11 @@ config RNG_OPTEE accessible to normal world but reserved and used by the OP-TEE to avoid the weakness of a software PRNG. -config RNG_STM32MP1 - bool "Enable random number generator for STM32MP1" - depends on ARCH_STM32MP +config RNG_STM32 + bool "Enable random number generator for STM32" + depends on ARCH_STM32 || ARCH_STM32MP help - Enable STM32MP1 rng driver. + Enable STM32 rng driver. config RNG_ROCKCHIP bool "Enable random number generator for rockchip crypto rng" diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile index 78f61051acf..192f911e155 100644 --- a/drivers/rng/Makefile +++ b/drivers/rng/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_RNG_SANDBOX) += sandbox_rng.o obj-$(CONFIG_RNG_MSM) += msm_rng.o obj-$(CONFIG_RNG_NPCM) += npcm_rng.o obj-$(CONFIG_RNG_OPTEE) += optee_rng.o -obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o +obj-$(CONFIG_RNG_STM32) += stm32_rng.o obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o obj-$(CONFIG_RNG_IPROC200) += iproc_rng200.o obj-$(CONFIG_RNG_SMCCC_TRNG) += smccc_trng.o diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c new file mode 100644 index 00000000000..89da78c6c8b --- /dev/null +++ b/drivers/rng/stm32_rng.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019, Linaro Limited + */ + +#define LOG_CATEGORY UCLASS_RNG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define RNG_CR 0x00 +#define RNG_CR_RNGEN BIT(2) +#define RNG_CR_CED BIT(5) +#define RNG_CR_CONDRST BIT(30) + +#define RNG_SR 0x04 +#define RNG_SR_SEIS BIT(6) +#define RNG_SR_CEIS BIT(5) +#define RNG_SR_SECS BIT(2) +#define RNG_SR_DRDY BIT(0) + +#define RNG_DR 0x08 + +struct stm32_rng_data { + bool has_cond_reset; +}; + +struct stm32_rng_plat { + fdt_addr_t base; + struct clk clk; + struct reset_ctl rst; + const struct stm32_rng_data *data; +}; + +static int stm32_rng_read(struct udevice *dev, void *data, size_t len) +{ + int retval, i; + u32 sr, count, reg; + size_t increment; + struct stm32_rng_plat *pdata = dev_get_plat(dev); + + while (len > 0) { + retval = readl_poll_timeout(pdata->base + RNG_SR, sr, + sr & RNG_SR_DRDY, 10000); + if (retval) + return retval; + + if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) { + /* As per SoC TRM */ + clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS); + for (i = 0; i < 12; i++) + readl(pdata->base + RNG_DR); + if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) { + log_err("RNG Noise"); + return -EIO; + } + /* start again */ + continue; + } + + /* + * Once the DRDY bit is set, the RNG_DR register can + * be read four consecutive times. + */ + count = 4; + while (len && count) { + reg = readl(pdata->base + RNG_DR); + memcpy(data, ®, min(len, sizeof(u32))); + increment = min(len, sizeof(u32)); + data += increment; + len -= increment; + count--; + } + } + + return 0; +} + +static int stm32_rng_init(struct stm32_rng_plat *pdata) +{ + int err; + u32 cr, sr; + + err = clk_enable(&pdata->clk); + if (err) + return err; + + cr = readl(pdata->base + RNG_CR); + + /* Disable CED */ + cr |= RNG_CR_CED; + if (pdata->data->has_cond_reset) { + cr |= RNG_CR_CONDRST; + writel(cr, pdata->base + RNG_CR); + cr &= ~RNG_CR_CONDRST; + writel(cr, pdata->base + RNG_CR); + err = readl_poll_timeout(pdata->base + RNG_CR, cr, + (!(cr & RNG_CR_CONDRST)), 10000); + if (err) + return err; + } + + /* clear error indicators */ + writel(0, pdata->base + RNG_SR); + + cr |= RNG_CR_RNGEN; + writel(cr, pdata->base + RNG_CR); + + err = readl_poll_timeout(pdata->base + RNG_SR, sr, + sr & RNG_SR_DRDY, 10000); + return err; +} + +static int stm32_rng_cleanup(struct stm32_rng_plat *pdata) +{ + writel(0, pdata->base + RNG_CR); + + return clk_disable(&pdata->clk); +} + +static int stm32_rng_probe(struct udevice *dev) +{ + struct stm32_rng_plat *pdata = dev_get_plat(dev); + + pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev); + + reset_assert(&pdata->rst); + udelay(20); + reset_deassert(&pdata->rst); + + return stm32_rng_init(pdata); +} + +static int stm32_rng_remove(struct udevice *dev) +{ + struct stm32_rng_plat *pdata = dev_get_plat(dev); + + return stm32_rng_cleanup(pdata); +} + +static int stm32_rng_of_to_plat(struct udevice *dev) +{ + struct stm32_rng_plat *pdata = dev_get_plat(dev); + int err; + + pdata->base = dev_read_addr(dev); + if (!pdata->base) + return -ENOMEM; + + err = clk_get_by_index(dev, 0, &pdata->clk); + if (err) + return err; + + err = reset_get_by_index(dev, 0, &pdata->rst); + if (err) + return err; + + return 0; +} + +static const struct dm_rng_ops stm32_rng_ops = { + .read = stm32_rng_read, +}; + +static const struct stm32_rng_data stm32mp13_rng_data = { + .has_cond_reset = true, +}; + +static const struct stm32_rng_data stm32_rng_data = { + .has_cond_reset = false, +}; + +static const struct udevice_id stm32_rng_match[] = { + {.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data}, + {.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data}, + {}, +}; + +U_BOOT_DRIVER(stm32_rng) = { + .name = "stm32-rng", + .id = UCLASS_RNG, + .of_match = stm32_rng_match, + .ops = &stm32_rng_ops, + .probe = stm32_rng_probe, + .remove = stm32_rng_remove, + .plat_auto = sizeof(struct stm32_rng_plat), + .of_to_plat = stm32_rng_of_to_plat, +}; diff --git a/drivers/rng/stm32mp1_rng.c b/drivers/rng/stm32mp1_rng.c deleted file mode 100644 index 89da78c6c8b..00000000000 --- a/drivers/rng/stm32mp1_rng.c +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2019, Linaro Limited - */ - -#define LOG_CATEGORY UCLASS_RNG - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define RNG_CR 0x00 -#define RNG_CR_RNGEN BIT(2) -#define RNG_CR_CED BIT(5) -#define RNG_CR_CONDRST BIT(30) - -#define RNG_SR 0x04 -#define RNG_SR_SEIS BIT(6) -#define RNG_SR_CEIS BIT(5) -#define RNG_SR_SECS BIT(2) -#define RNG_SR_DRDY BIT(0) - -#define RNG_DR 0x08 - -struct stm32_rng_data { - bool has_cond_reset; -}; - -struct stm32_rng_plat { - fdt_addr_t base; - struct clk clk; - struct reset_ctl rst; - const struct stm32_rng_data *data; -}; - -static int stm32_rng_read(struct udevice *dev, void *data, size_t len) -{ - int retval, i; - u32 sr, count, reg; - size_t increment; - struct stm32_rng_plat *pdata = dev_get_plat(dev); - - while (len > 0) { - retval = readl_poll_timeout(pdata->base + RNG_SR, sr, - sr & RNG_SR_DRDY, 10000); - if (retval) - return retval; - - if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) { - /* As per SoC TRM */ - clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS); - for (i = 0; i < 12; i++) - readl(pdata->base + RNG_DR); - if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) { - log_err("RNG Noise"); - return -EIO; - } - /* start again */ - continue; - } - - /* - * Once the DRDY bit is set, the RNG_DR register can - * be read four consecutive times. - */ - count = 4; - while (len && count) { - reg = readl(pdata->base + RNG_DR); - memcpy(data, ®, min(len, sizeof(u32))); - increment = min(len, sizeof(u32)); - data += increment; - len -= increment; - count--; - } - } - - return 0; -} - -static int stm32_rng_init(struct stm32_rng_plat *pdata) -{ - int err; - u32 cr, sr; - - err = clk_enable(&pdata->clk); - if (err) - return err; - - cr = readl(pdata->base + RNG_CR); - - /* Disable CED */ - cr |= RNG_CR_CED; - if (pdata->data->has_cond_reset) { - cr |= RNG_CR_CONDRST; - writel(cr, pdata->base + RNG_CR); - cr &= ~RNG_CR_CONDRST; - writel(cr, pdata->base + RNG_CR); - err = readl_poll_timeout(pdata->base + RNG_CR, cr, - (!(cr & RNG_CR_CONDRST)), 10000); - if (err) - return err; - } - - /* clear error indicators */ - writel(0, pdata->base + RNG_SR); - - cr |= RNG_CR_RNGEN; - writel(cr, pdata->base + RNG_CR); - - err = readl_poll_timeout(pdata->base + RNG_SR, sr, - sr & RNG_SR_DRDY, 10000); - return err; -} - -static int stm32_rng_cleanup(struct stm32_rng_plat *pdata) -{ - writel(0, pdata->base + RNG_CR); - - return clk_disable(&pdata->clk); -} - -static int stm32_rng_probe(struct udevice *dev) -{ - struct stm32_rng_plat *pdata = dev_get_plat(dev); - - pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev); - - reset_assert(&pdata->rst); - udelay(20); - reset_deassert(&pdata->rst); - - return stm32_rng_init(pdata); -} - -static int stm32_rng_remove(struct udevice *dev) -{ - struct stm32_rng_plat *pdata = dev_get_plat(dev); - - return stm32_rng_cleanup(pdata); -} - -static int stm32_rng_of_to_plat(struct udevice *dev) -{ - struct stm32_rng_plat *pdata = dev_get_plat(dev); - int err; - - pdata->base = dev_read_addr(dev); - if (!pdata->base) - return -ENOMEM; - - err = clk_get_by_index(dev, 0, &pdata->clk); - if (err) - return err; - - err = reset_get_by_index(dev, 0, &pdata->rst); - if (err) - return err; - - return 0; -} - -static const struct dm_rng_ops stm32_rng_ops = { - .read = stm32_rng_read, -}; - -static const struct stm32_rng_data stm32mp13_rng_data = { - .has_cond_reset = true, -}; - -static const struct stm32_rng_data stm32_rng_data = { - .has_cond_reset = false, -}; - -static const struct udevice_id stm32_rng_match[] = { - {.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data}, - {.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data}, - {}, -}; - -U_BOOT_DRIVER(stm32_rng) = { - .name = "stm32-rng", - .id = UCLASS_RNG, - .of_match = stm32_rng_match, - .ops = &stm32_rng_ops, - .probe = stm32_rng_probe, - .remove = stm32_rng_remove, - .plat_auto = sizeof(struct stm32_rng_plat), - .of_to_plat = stm32_rng_of_to_plat, -}; -- cgit v1.3.1 From 2d2574b4055f86f2fac57c6322e6487f15524baf Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Tue, 19 Sep 2023 17:27:55 +0200 Subject: rng: stm32: Implement configurable RNG clock error detection RNG clock error detection is now enabled if the "clock-error-detect" property is set in the device tree. Signed-off-by: Gatien Chevallier Reviewed-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/rng/stm32_rng.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c index 89da78c6c8b..ada5d922141 100644 --- a/drivers/rng/stm32_rng.c +++ b/drivers/rng/stm32_rng.c @@ -40,6 +40,7 @@ struct stm32_rng_plat { struct clk clk; struct reset_ctl rst; const struct stm32_rng_data *data; + bool ced; }; static int stm32_rng_read(struct udevice *dev, void *data, size_t len) @@ -97,25 +98,34 @@ static int stm32_rng_init(struct stm32_rng_plat *pdata) cr = readl(pdata->base + RNG_CR); - /* Disable CED */ - cr |= RNG_CR_CED; if (pdata->data->has_cond_reset) { cr |= RNG_CR_CONDRST; + if (pdata->ced) + cr &= ~RNG_CR_CED; + else + cr |= RNG_CR_CED; writel(cr, pdata->base + RNG_CR); cr &= ~RNG_CR_CONDRST; + cr |= RNG_CR_RNGEN; writel(cr, pdata->base + RNG_CR); err = readl_poll_timeout(pdata->base + RNG_CR, cr, (!(cr & RNG_CR_CONDRST)), 10000); if (err) return err; + } else { + if (pdata->ced) + cr &= ~RNG_CR_CED; + else + cr |= RNG_CR_CED; + + cr |= RNG_CR_RNGEN; + + writel(cr, pdata->base + RNG_CR); } /* clear error indicators */ writel(0, pdata->base + RNG_SR); - cr |= RNG_CR_RNGEN; - writel(cr, pdata->base + RNG_CR); - err = readl_poll_timeout(pdata->base + RNG_SR, sr, sr & RNG_SR_DRDY, 10000); return err; @@ -165,6 +175,8 @@ static int stm32_rng_of_to_plat(struct udevice *dev) if (err) return err; + pdata->ced = dev_read_bool(dev, "clock-error-detect"); + return 0; } -- cgit v1.3.1 From 01af3636230656cdcba7a1c625c17a5c32a3fb69 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Tue, 19 Sep 2023 17:27:56 +0200 Subject: rng: stm32: add RNG clock frequency restraint In order to ensure a good RNG quality and compatibility with certified RNG configuration, add RNG clock frequency restraint. Signed-off-by: Gatien Chevallier Reviewed-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/rng/stm32_rng.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c index ada5d922141..f943acd7d22 100644 --- a/drivers/rng/stm32_rng.c +++ b/drivers/rng/stm32_rng.c @@ -18,10 +18,11 @@ #include #include -#define RNG_CR 0x00 -#define RNG_CR_RNGEN BIT(2) -#define RNG_CR_CED BIT(5) -#define RNG_CR_CONDRST BIT(30) +#define RNG_CR 0x00 +#define RNG_CR_RNGEN BIT(2) +#define RNG_CR_CED BIT(5) +#define RNG_CR_CLKDIV_SHIFT 16 +#define RNG_CR_CONDRST BIT(30) #define RNG_SR 0x04 #define RNG_SR_SEIS BIT(6) @@ -31,7 +32,15 @@ #define RNG_DR 0x08 +/* + * struct stm32_rng_data - RNG compat data + * + * @max_clock_rate: Max RNG clock frequency, in Hertz + * @has_cond_reset: True if conditionnal reset is supported + * + */ struct stm32_rng_data { + uint max_clock_rate; bool has_cond_reset; }; @@ -87,6 +96,26 @@ static int stm32_rng_read(struct udevice *dev, void *data, size_t len) return 0; } +static uint stm32_rng_clock_freq_restrain(struct stm32_rng_plat *pdata) +{ + ulong clock_rate = 0; + uint clock_div = 0; + + clock_rate = clk_get_rate(&pdata->clk); + + /* + * 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) > pdata->data->max_clock_rate) + clock_div++; + + log_debug("RNG clk rate : %lu\n", clk_get_rate(&pdata->clk) >> clock_div); + + return clock_div; +} + static int stm32_rng_init(struct stm32_rng_plat *pdata) { int err; @@ -99,7 +128,9 @@ static int stm32_rng_init(struct stm32_rng_plat *pdata) cr = readl(pdata->base + RNG_CR); if (pdata->data->has_cond_reset) { - cr |= RNG_CR_CONDRST; + uint clock_div = stm32_rng_clock_freq_restrain(pdata); + + cr |= RNG_CR_CONDRST | (clock_div << RNG_CR_CLKDIV_SHIFT); if (pdata->ced) cr &= ~RNG_CR_CED; else @@ -186,10 +217,12 @@ static const struct dm_rng_ops stm32_rng_ops = { static const struct stm32_rng_data stm32mp13_rng_data = { .has_cond_reset = true, + .max_clock_rate = 48000000, }; static const struct stm32_rng_data stm32_rng_data = { .has_cond_reset = false, + .max_clock_rate = 3000000, }; static const struct udevice_id stm32_rng_match[] = { -- cgit v1.3.1 From 6032292534e0f47012edd76cd88b2c952856f928 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Tue, 19 Sep 2023 17:27:57 +0200 Subject: rng: stm32: add error concealment sequence Seed errors can occur when using the hardware RNG. Implement the sequences to handle them. This avoids irrecoverable RNG state. Try to conceal seed errors when possible. If, despite the error concealing tries, a seed error is still present, then return an error. A clock error does not compromise the hardware block and data can still be read from RNG_DR. Just warn that the RNG clock is too slow and clear RNG_SR. Signed-off-by: Gatien Chevallier Reviewed-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/rng/stm32_rng.c | 163 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 140 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c index f943acd7d22..b1a790b217f 100644 --- a/drivers/rng/stm32_rng.c +++ b/drivers/rng/stm32_rng.c @@ -32,6 +32,8 @@ #define RNG_DR 0x08 +#define RNG_NB_RECOVER_TRIES 3 + /* * struct stm32_rng_data - RNG compat data * @@ -52,45 +54,160 @@ struct stm32_rng_plat { bool ced; }; +/* + * Extracts from the STM32 RNG specification when RNG supports CONDRST. + * + * When a noise source (or seed) error occurs, the RNG stops generating + * random numbers and sets to “1” both SEIS and SECS bits to indicate + * that a seed error occurred. (...) + * + * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield + * description for details). This step is needed only if SECS is set. + * Indeed, when SEIS is set and SECS is cleared it means RNG performed + * the reset automatically (auto-reset). + * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST + * to be cleared in the RNG_CR register, then confirm that SEIS is + * cleared in the RNG_SR register. Otherwise just clear SEIS bit in + * the RNG_SR register. + * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be + * cleared by RNG. The random number generation is now back to normal. + */ +static int stm32_rng_conceal_seed_error_cond_reset(struct stm32_rng_plat *pdata) +{ + u32 sr = readl_relaxed(pdata->base + RNG_SR); + u32 cr = readl_relaxed(pdata->base + RNG_CR); + int err; + + if (sr & RNG_SR_SECS) { + /* Conceal by resetting the subsystem (step 1.) */ + writel_relaxed(cr | RNG_CR_CONDRST, pdata->base + RNG_CR); + writel_relaxed(cr & ~RNG_CR_CONDRST, pdata->base + RNG_CR); + } else { + /* RNG auto-reset (step 2.) */ + writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR); + return 0; + } + + err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_CR_CONDRST), 100000); + if (err) { + log_err("%s: timeout %x\n", __func__, sr); + return err; + } + + /* Check SEIS is cleared (step 2.) */ + if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS) + return -EINVAL; + + err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_SR_SECS), 100000); + if (err) { + log_err("%s: timeout %x\n", __func__, sr); + return err; + } + + return 0; +} + +/* + * Extracts from the STM32 RNG specification, when CONDRST is not supported + * + * When a noise source (or seed) error occurs, the RNG stops generating + * random numbers and sets to “1” both SEIS and SECS bits to indicate + * that a seed error occurred. (...) + * + * The following sequence shall be used to fully recover from a seed + * error after the RNG initialization: + * 1. Clear the SEIS bit by writing it to “0”. + * 2. Read out 12 words from the RNG_DR register, and discard each of + * them in order to clean the pipeline. + * 3. Confirm that SEIS is still cleared. Random number generation is + * back to normal. + */ +static int stm32_rng_conceal_seed_error_sw_reset(struct stm32_rng_plat *pdata) +{ + uint i = 0; + u32 sr = readl_relaxed(pdata->base + RNG_SR); + + writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR); + + for (i = 12; i != 0; i--) + (void)readl_relaxed(pdata->base + RNG_DR); + + if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS) + return -EINVAL; + + return 0; +} + +static int stm32_rng_conceal_seed_error(struct stm32_rng_plat *pdata) +{ + log_debug("Concealing RNG seed error\n"); + + if (pdata->data->has_cond_reset) + return stm32_rng_conceal_seed_error_cond_reset(pdata); + else + return stm32_rng_conceal_seed_error_sw_reset(pdata); +}; + static int stm32_rng_read(struct udevice *dev, void *data, size_t len) { - int retval, i; - u32 sr, count, reg; + int retval; + u32 sr, reg; size_t increment; struct stm32_rng_plat *pdata = dev_get_plat(dev); + uint tries = 0; while (len > 0) { retval = readl_poll_timeout(pdata->base + RNG_SR, sr, - sr & RNG_SR_DRDY, 10000); - if (retval) + sr, 10000); + if (retval) { + log_err("%s: Timeout RNG no data", __func__); return retval; + } - if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) { - /* As per SoC TRM */ - clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS); - for (i = 0; i < 12; i++) - readl(pdata->base + RNG_DR); - if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) { - log_err("RNG Noise"); - return -EIO; + if (sr != RNG_SR_DRDY) { + if (sr & RNG_SR_SEIS) { + retval = stm32_rng_conceal_seed_error(pdata); + tries++; + if (retval || tries > RNG_NB_RECOVER_TRIES) { + log_err("%s: Couldn't recover from seed error", __func__); + return -ENOTRECOVERABLE; + } + + /* Start again */ + continue; + } + + if (sr & RNG_SR_CEIS) { + log_info("RNG clock too slow"); + writel_relaxed(0, pdata->base + RNG_SR); } - /* start again */ - continue; } /* * Once the DRDY bit is set, the RNG_DR register can - * be read four consecutive times. + * be read up to four consecutive times. */ - count = 4; - while (len && count) { - reg = readl(pdata->base + RNG_DR); - memcpy(data, ®, min(len, sizeof(u32))); - increment = min(len, sizeof(u32)); - data += increment; - len -= increment; - count--; + reg = readl(pdata->base + RNG_DR); + /* Late seed error case: DR being 0 is an error status */ + if (!reg) { + retval = stm32_rng_conceal_seed_error(pdata); + tries++; + + if (retval || tries > RNG_NB_RECOVER_TRIES) { + log_err("%s: Couldn't recover from seed error", __func__); + return -ENOTRECOVERABLE; + } + + /* Start again */ + continue; } + + increment = min(len, sizeof(u32)); + memcpy(data, ®, increment); + data += increment; + len -= increment; + + tries = 0; } return 0; -- cgit v1.3.1 From e077d7f61309d83fa94c55b17bfccc255b4467fb Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Tue, 19 Sep 2023 17:27:58 +0200 Subject: rng: stm32: Implement custom RNG configuration support STM32 RNG configuration should best fit the requirements of the platform. Therefore, put a platform-specific RNG configuration field in the platform data. Default RNG configuration for STM32MP13 is the NIST certified configuration [1]. While there, fix and the RNG init sequence to support all RNG versions. [1] https://csrc.nist.gov/projects/cryptographic-module-validation-program/entropy-validations/certificate/53 Signed-off-by: Gatien Chevallier Reviewed-by: Patrick Delaunay Reviewed-by: Patrice Chotard --- drivers/rng/stm32_rng.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c index b1a790b217f..c397b4d95cd 100644 --- a/drivers/rng/stm32_rng.c +++ b/drivers/rng/stm32_rng.c @@ -21,8 +21,15 @@ #define RNG_CR 0x00 #define RNG_CR_RNGEN BIT(2) #define RNG_CR_CED BIT(5) +#define RNG_CR_CONFIG1 GENMASK(11, 8) +#define RNG_CR_NISTC BIT(12) +#define RNG_CR_CONFIG2 GENMASK(15, 13) #define RNG_CR_CLKDIV_SHIFT 16 +#define RNG_CR_CLKDIV GENMASK(19, 16) +#define RNG_CR_CONFIG3 GENMASK(25, 20) #define RNG_CR_CONDRST BIT(30) +#define RNG_CR_ENTROPY_SRC_MASK (RNG_CR_CONFIG1 | RNG_CR_NISTC | RNG_CR_CONFIG2 | RNG_CR_CONFIG3) +#define RNG_CR_CONFIG_MASK (RNG_CR_ENTROPY_SRC_MASK | RNG_CR_CED | RNG_CR_CLKDIV) #define RNG_SR 0x04 #define RNG_SR_SEIS BIT(6) @@ -32,17 +39,28 @@ #define RNG_DR 0x08 +#define RNG_NSCR 0x0C +#define RNG_NSCR_MASK GENMASK(17, 0) + +#define RNG_HTCR 0x10 + #define RNG_NB_RECOVER_TRIES 3 /* * struct stm32_rng_data - RNG compat data * * @max_clock_rate: Max RNG clock frequency, in Hertz + * @cr: Entropy source configuration + * @nscr: Noice sources control configuration + * @htcr: Health tests configuration * @has_cond_reset: True if conditionnal reset is supported * */ struct stm32_rng_data { uint max_clock_rate; + u32 cr; + u32 nscr; + u32 htcr; bool has_cond_reset; }; @@ -244,28 +262,48 @@ static int stm32_rng_init(struct stm32_rng_plat *pdata) cr = readl(pdata->base + RNG_CR); - if (pdata->data->has_cond_reset) { + /* + * Keep default RNG configuration if none was specified, that is when conf.cr is set to 0. + */ + if (pdata->data->has_cond_reset && pdata->data->cr) { uint clock_div = stm32_rng_clock_freq_restrain(pdata); - cr |= RNG_CR_CONDRST | (clock_div << RNG_CR_CLKDIV_SHIFT); + cr &= ~RNG_CR_CONFIG_MASK; + cr |= RNG_CR_CONDRST | (pdata->data->cr & RNG_CR_ENTROPY_SRC_MASK) | + (clock_div << RNG_CR_CLKDIV_SHIFT); if (pdata->ced) cr &= ~RNG_CR_CED; else cr |= RNG_CR_CED; writel(cr, pdata->base + RNG_CR); + + /* Health tests and noise control registers */ + writel_relaxed(pdata->data->htcr, pdata->base + RNG_HTCR); + writel_relaxed(pdata->data->nscr & RNG_NSCR_MASK, pdata->base + RNG_NSCR); + cr &= ~RNG_CR_CONDRST; cr |= RNG_CR_RNGEN; writel(cr, pdata->base + RNG_CR); err = readl_poll_timeout(pdata->base + RNG_CR, cr, (!(cr & RNG_CR_CONDRST)), 10000); - if (err) + if (err) { + log_err("%s: Timeout!", __func__); return err; + } } else { + if (pdata->data->has_cond_reset) + cr |= RNG_CR_CONDRST; + if (pdata->ced) cr &= ~RNG_CR_CED; else cr |= RNG_CR_CED; + writel(cr, pdata->base + RNG_CR); + + if (pdata->data->has_cond_reset) + cr &= ~RNG_CR_CONDRST; + cr |= RNG_CR_RNGEN; writel(cr, pdata->base + RNG_CR); @@ -276,6 +314,9 @@ static int stm32_rng_init(struct stm32_rng_plat *pdata) err = readl_poll_timeout(pdata->base + RNG_SR, sr, sr & RNG_SR_DRDY, 10000); + if (err) + log_err("%s: Timeout!", __func__); + return err; } @@ -335,11 +376,18 @@ static const struct dm_rng_ops stm32_rng_ops = { static const struct stm32_rng_data stm32mp13_rng_data = { .has_cond_reset = true, .max_clock_rate = 48000000, + .htcr = 0x969D, + .nscr = 0x2B5BB, + .cr = 0xF00D00, }; static const struct stm32_rng_data stm32_rng_data = { .has_cond_reset = false, .max_clock_rate = 3000000, + /* Not supported */ + .htcr = 0, + .nscr = 0, + .cr = 0, }; static const struct udevice_id stm32_rng_match[] = { -- cgit v1.3.1 From 719a8759cde676119645ce16c858301cee4649cd Mon Sep 17 00:00:00 2001 From: Harald Seiler Date: Wed, 27 Sep 2023 14:44:40 +0200 Subject: ram: stm32mp1: Only print RAM config with CONFIG_SPL_DISPLAY_PRINT Ensure that the RAM configuration line is only printed when CONFIG_SPL_DISPLAY_PRINT is set. Signed-off-by: Harald Seiler Reviewed-by: Patrice Chotard --- drivers/ram/stm32mp1/stm32mp1_ram.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ram/stm32mp1/stm32mp1_ram.c b/drivers/ram/stm32mp1/stm32mp1_ram.c index a6c19af9722..2808d07c3ae 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ram.c +++ b/drivers/ram/stm32mp1/stm32mp1_ram.c @@ -126,7 +126,8 @@ static int stm32mp1_ddr_setup(struct udevice *dev) dev_dbg(dev, "no st,mem-name\n"); return -EINVAL; } - printf("RAM: %s\n", config.info.name); + if (CONFIG_IS_ENABLED(DISPLAY_PRINT)) + printf("RAM: %s\n", config.info.name); for (idx = 0; idx < ARRAY_SIZE(param); idx++) { ret = ofnode_read_u32_array(node, param[idx].name, -- cgit v1.3.1 From 062ee99e0a72cf0714fc5fffb567fa502e8ee855 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 29 Sep 2023 13:34:37 +0200 Subject: clk: stm32mp1: Add support for USART1 clock Add USART1 clock parents and mux configuration. This allows support for configuring the USART1 as the serial console in SPL and U-Boot via device tree. Without this patch the SPL with usart1 serial console enabled crashes because it can not find the clock specified in the device tree for usart1. Signed-off-by: Anatolij Gustschin Reviewed-by: Patrice Chotard --- drivers/clk/stm32/clk-stm32mp1.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/stm32/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c index 61502876949..f3ac8c75831 100644 --- a/drivers/clk/stm32/clk-stm32mp1.c +++ b/drivers/clk/stm32/clk-stm32mp1.c @@ -72,6 +72,7 @@ DECLARE_GLOBAL_DATA_PTR; #define RCC_PLL2CSGR 0xA4 #define RCC_I2C46CKSELR 0xC0 #define RCC_SPI6CKSELR 0xC4 +#define RCC_UART1CKSELR 0xC8 #define RCC_CPERCKSELR 0xD0 #define RCC_STGENCKSELR 0xD4 #define RCC_DDRITFCR 0xD8 @@ -317,6 +318,7 @@ enum stm32mp1_parent_sel { _SPI45_SEL, _SPI6_SEL, _RTC_SEL, + _UART1_SEL, _PARENT_SEL_NB, _UNKNOWN_SEL = 0xff, }; @@ -557,6 +559,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 0, SPI6_K, _SPI6_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 3, I2C6_K, _I2C46_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 4, USART1_K, _UART1_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 8, RTCAPB, _PCLK5), STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 16, BSEC, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL), @@ -602,6 +605,8 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { static const u8 i2c12_parents[] = {_PCLK1, _PLL4_R, _HSI_KER, _CSI_KER}; static const u8 i2c35_parents[] = {_PCLK1, _PLL4_R, _HSI_KER, _CSI_KER}; static const u8 i2c46_parents[] = {_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER}; +static const u8 uart1_parents[] = {_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER, + _PLL4_Q, _HSE_KER}; static const u8 uart6_parents[] = {_PCLK2, _PLL4_Q, _HSI_KER, _CSI_KER, _HSE_KER}; static const u8 uart24_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, @@ -659,6 +664,7 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { STM32MP1_CLK_PARENT(_RTC_SEL, RCC_BDCR, RCC_BDCR_RTCSRC_SHIFT, (RCC_BDCR_RTCSRC_MASK >> RCC_BDCR_RTCSRC_SHIFT), rtc_parents), + STM32MP1_CLK_PARENT(_UART1_SEL, RCC_UART1CKSELR, 0, 0x7, uart1_parents), }; #ifdef STM32MP1_CLOCK_TREE_INIT @@ -786,6 +792,7 @@ char * const stm32mp1_clk_parent_sel_name[_PARENT_SEL_NB] = { [_SPI1_SEL] = "SPI1", [_SPI45_SEL] = "SPI45", [_RTC_SEL] = "RTC", + [_UART1_SEL] = "UART1", }; static const struct stm32mp1_clk_data stm32mp1_data = { -- cgit v1.3.1