summaryrefslogtreecommitdiff
path: root/drivers/rng/stm32_rng.c
diff options
context:
space:
mode:
authorGatien Chevallier <gatien.chevallier@foss.st.com>2023-09-19 17:27:53 +0200
committerPatrice Chotard <patrice.chotard@foss.st.com>2023-10-04 13:26:02 +0200
commit77e0c6087923574579abe1a04538bb4982e33d55 (patch)
tree1ca3529caca577fc7fd93a41929d6ebda907e04c /drivers/rng/stm32_rng.c
parent388f872691f6d3055b472cf71b73628da3303b77 (diff)
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 <gatien.chevallier@foss.st.com> Reviewed-by: Grzegorz Szymaszek <gszymaszek@short.pl> Reviewed-by: Patrick Delaunay <patrick.delaunay@foss.st.com> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
Diffstat (limited to 'drivers/rng/stm32_rng.c')
-rw-r--r--drivers/rng/stm32_rng.c198
1 files changed, 198 insertions, 0 deletions
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 <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <log.h>
+#include <reset.h>
+#include <rng.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#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, &reg, 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,
+};